summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/tests/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/tests/ui')
-rw-r--r--src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs61
-rw-r--r--src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr147
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_without_reason.rs14
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr23
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.fixed67
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.rs67
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_letter_range.stderr100
-rw-r--r--src/tools/clippy/tests/ui/approx_const.rs64
-rw-r--r--src/tools/clippy/tests/ui/approx_const.stderr187
-rw-r--r--src/tools/clippy/tests/ui/arithmetic.fixed27
-rw-r--r--src/tools/clippy/tests/ui/arithmetic.rs27
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.rs20
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.stderr27
-rw-r--r--src/tools/clippy/tests/ui/as_underscore.fixed13
-rw-r--r--src/tools/clippy/tests/ui/as_underscore.rs13
-rw-r--r--src/tools/clippy/tests/ui/as_underscore.stderr20
-rw-r--r--src/tools/clippy/tests/ui/asm_syntax.rs34
-rw-r--r--src/tools/clippy/tests/ui/asm_syntax.stderr44
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_constants.rs39
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_constants.stderr75
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.fixed69
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.rs69
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.stderr40
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.fixed32
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.rs32
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.stderr70
-rw-r--r--src/tools/clippy/tests/ui/assign_ops2.rs55
-rw-r--r--src/tools/clippy/tests/ui/assign_ops2.stderr146
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed78
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs78
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr96
-rw-r--r--src/tools/clippy/tests/ui/attrs.rs45
-rw-r--r--src/tools/clippy/tests/ui/attrs.stderr24
-rw-r--r--src/tools/clippy/tests/ui/author.rs4
-rw-r--r--src/tools/clippy/tests/ui/author.stdout14
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.rs24
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout64
-rw-r--r--src/tools/clippy/tests/ui/author/call.rs4
-rw-r--r--src/tools/clippy/tests/ui/author/call.stdout16
-rw-r--r--src/tools/clippy/tests/ui/author/if.rs17
-rw-r--r--src/tools/clippy/tests/ui/author/if.stdout50
-rw-r--r--src/tools/clippy/tests/ui/author/issue_3849.rs14
-rw-r--r--src/tools/clippy/tests/ui/author/issue_3849.stdout14
-rw-r--r--src/tools/clippy/tests/ui/author/loop.rs36
-rw-r--r--src/tools/clippy/tests/ui/author/loop.stdout113
-rw-r--r--src/tools/clippy/tests/ui/author/matches.rs13
-rw-r--r--src/tools/clippy/tests/ui/author/matches.stdout38
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.rs5
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.stdout12
-rw-r--r--src/tools/clippy/tests/ui/author/struct.rs40
-rw-r--r--src/tools/clippy/tests/ui/author/struct.stdout64
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs8
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs6
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_rules.rs142
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs60
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs8
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/option_helpers.rs64
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs101
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs88
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs74
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs18
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs32
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/test_macro.rs11
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs15
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs27
-rw-r--r--src/tools/clippy/tests/ui/await_holding_lock.rs192
-rw-r--r--src/tools/clippy/tests/ui/await_holding_lock.stderr208
-rw-r--r--src/tools/clippy/tests/ui/await_holding_refcell_ref.rs85
-rw-r--r--src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr101
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map.fixed25
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map.rs25
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map.stderr26
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed62
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs62
-rw-r--r--src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr91
-rw-r--r--src/tools/clippy/tests/ui/bit_masks.rs63
-rw-r--r--src/tools/clippy/tests/ui/bit_masks.stderr110
-rw-r--r--src/tools/clippy/tests/ui/blacklisted_name.rs57
-rw-r--r--src/tools/clippy/tests/ui/blacklisted_name.stderr88
-rw-r--r--src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.rs8
-rw-r--r--src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr27
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed65
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions.rs65
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr34
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs64
-rw-r--r--src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr24
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.rs122
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.stderr136
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.fixed167
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.rs167
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.stderr136
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.fixed10
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.rs10
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.stderr16
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed22
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs22
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr16
-rw-r--r--src/tools/clippy/tests/ui/borrow_box.rs115
-rw-r--r--src/tools/clippy/tests/ui/borrow_box.stderr68
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.fixed59
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.rs59
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.stderr22
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.rs10
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr18
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs17
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs101
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr75
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs104
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr115
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs202
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr123
-rw-r--r--src/tools/clippy/tests/ui/box_collection.rs56
-rw-r--r--src/tools/clippy/tests/ui/box_collection.stderr75
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.rs209
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.stderr28
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs95
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs223
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr143
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs114
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr121
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs119
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr155
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs155
-rw-r--r--src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr101
-rw-r--r--src/tools/clippy/tests/ui/builtin_type_shadow.rs9
-rw-r--r--src/tools/clippy/tests/ui/builtin_type_shadow.stderr24
-rw-r--r--src/tools/clippy/tests/ui/bytecount.rs26
-rw-r--r--src/tools/clippy/tests/ui/bytecount.stderr26
-rw-r--r--src/tools/clippy/tests/ui/bytes_count_to_len.fixed34
-rw-r--r--src/tools/clippy/tests/ui/bytes_count_to_len.rs34
-rw-r--r--src/tools/clippy/tests/ui/bytes_count_to_len.stderr28
-rw-r--r--src/tools/clippy/tests/ui/bytes_nth.fixed11
-rw-r--r--src/tools/clippy/tests/ui/bytes_nth.rs11
-rw-r--r--src/tools/clippy/tests/ui/bytes_nth.stderr22
-rw-r--r--src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs44
-rw-r--r--src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr43
-rw-r--r--src/tools/clippy/tests/ui/cast.rs262
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr210
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed29
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs29
-rw-r--r--src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr100
-rw-r--r--src/tools/clippy/tests/ui/cast_alignment.rs51
-rw-r--r--src/tools/clippy/tests/ui/cast_alignment.stderr28
-rw-r--r--src/tools/clippy/tests/ui/cast_enum_constructor.rs17
-rw-r--r--src/tools/clippy/tests/ui/cast_enum_constructor.stderr16
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.fixed42
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.rs42
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.stderr82
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_float.fixed45
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_float.rs45
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_float.stderr70
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_integer.fixed47
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_integer.rs47
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_integer.stderr118
-rw-r--r--src/tools/clippy/tests/ui/cast_ref_to_mut.rs31
-rw-r--r--src/tools/clippy/tests/ui/cast_ref_to_mut.stderr22
-rw-r--r--src/tools/clippy/tests/ui/cast_size.rs35
-rw-r--r--src/tools/clippy/tests/ui/cast_size.stderr116
-rw-r--r--src/tools/clippy/tests/ui/cast_size_32bit.rs35
-rw-r--r--src/tools/clippy/tests/ui/cast_size_32bit.stderr118
-rw-r--r--src/tools/clippy/tests/ui/cast_slice_different_sizes.rs82
-rw-r--r--src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr121
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed31
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs31
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr16
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8.rs5
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8.stderr11
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed10
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs10
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr35
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.fixed79
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.rs79
-rw-r--r--src/tools/clippy/tests/ui/checked_conversions.stderr100
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs54
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr211
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs15
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr31
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs102
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr167
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy.fixed74
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy.rs74
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy.stderr52
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy_impl.rs22
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed15
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.rs15
-rw-r--r--src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr34
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.rs34
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.stderr148
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.rs17
-rw-r--r--src/tools/clippy/tests/ui/cmp_null.stderr16
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed93
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs93
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr46
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/comparison_flip.fixed29
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/comparison_flip.rs29
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/comparison_flip.stderr18
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed72
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs72
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr40
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs75
-rw-r--r--src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr22
-rw-r--r--src/tools/clippy/tests/ui/cognitive_complexity.rs395
-rw-r--r--src/tools/clippy/tests/ui/cognitive_complexity.stderr139
-rw-r--r--src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs15
-rw-r--r--src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr11
-rw-r--r--src/tools/clippy/tests/ui/collapsible_else_if.fixed84
-rw-r--r--src/tools/clippy/tests/ui/collapsible_else_if.rs100
-rw-r--r--src/tools/clippy/tests/ui/collapsible_else_if.stderr163
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.fixed148
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.rs164
-rw-r--r--src/tools/clippy/tests/ui/collapsible_if.stderr130
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match.rs265
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match.stderr179
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match2.rs87
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match2.stderr97
-rw-r--r--src/tools/clippy/tests/ui/comparison_chain.rs234
-rw-r--r--src/tools/clippy/tests/ui/comparison_chain.stderr97
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.fixed23
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.rs23
-rw-r--r--src/tools/clippy/tests/ui/comparison_to_empty.stderr28
-rw-r--r--src/tools/clippy/tests/ui/copy_iterator.rs21
-rw-r--r--src/tools/clippy/tests/ui/copy_iterator.stderr17
-rw-r--r--src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-7272-aux.rs14
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-7868-aux.rs3
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs4
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs38
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs15
-rw-r--r--src/tools/clippy/tests/ui/crashes/cc_seme.rs27
-rw-r--r--src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-1588.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-1782.rs26
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-1969.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2499.rs26
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2594.rs20
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2727.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2760.rs23
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2774.rs27
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2774.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2862.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-2865.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3151.rs15
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3462.rs23
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-360.rs12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-360.stderr25
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3717.rs10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3717.stderr22
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3741.rs10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3747.rs17
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3891.rs3
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3891.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3969.rs50
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3969.stderr34
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4121.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4545.rs14
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4579.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4671.rs21
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4727.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4760.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4775.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-4968.rs21
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5207.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5223.rs15
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5238.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5389.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5497.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5497.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5579.rs17
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5835.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5835.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5872.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5872.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-5944.rs14
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6139.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6153.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6179.rs21
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6250.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6250.stderr30
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6251.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6251.stderr41
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.rs14
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.stderr36
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6254.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6254.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.rs15
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.stderr13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6256.rs15
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6256.stderr14
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6332.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6539.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6792.rs20
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6793.rs23
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6840.rs31
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-700.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7012.rs17
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7126.rs14
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7169.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7169.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7231.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7272.rs12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7340.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7410.rs32
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7423.rs13
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7868.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7868.stderr11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7869.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7869.stderr15
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7934.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8250.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8250.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8386.rs3
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8681.rs10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8821.rs8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8821.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8850.rs27
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-8850.stderr45
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9041.rs8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9041.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9238.rs12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-9242.rs8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.rs10
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.stderr8
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs19
-rw-r--r--src/tools/clippy/tests/ui/crashes/if_same_then_else.rs16
-rw-r--r--src/tools/clippy/tests/ui/crashes/implements-trait.rs5
-rw-r--r--src/tools/clippy/tests/ui/crashes/inherent_impl.rs26
-rw-r--r--src/tools/clippy/tests/ui/crashes/issue-825.rs25
-rw-r--r--src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs28
-rw-r--r--src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs18
-rw-r--r--src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs34
-rw-r--r--src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs20
-rw-r--r--src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr14
-rw-r--r--src/tools/clippy/tests/ui/crashes/regressions.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/returns.rs23
-rw-r--r--src/tools/clippy/tests/ui/crashes/shadow.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/single-match-else.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/third-party/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui/crashes/third-party/conf_allowlisted.rs1
-rw-r--r--src/tools/clippy/tests/ui/crashes/trivial_bounds.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs16
-rw-r--r--src/tools/clippy/tests/ui/crate_in_macro_def.fixed56
-rw-r--r--src/tools/clippy/tests/ui/crate_in_macro_def.rs56
-rw-r--r--src/tools/clippy/tests/ui/crate_in_macro_def.stderr10
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs11
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr11
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs33
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs14
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs6
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr11
-rw-r--r--src/tools/clippy/tests/ui/create_dir.fixed17
-rw-r--r--src/tools/clippy/tests/ui/create_dir.rs17
-rw-r--r--src/tools/clippy/tests/ui/create_dir.stderr16
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro.rs60
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro.stderr146
-rw-r--r--src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs133
-rw-r--r--src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr172
-rw-r--r--src/tools/clippy/tests/ui/decimal_literal_representation.fixed27
-rw-r--r--src/tools/clippy/tests/ui/decimal_literal_representation.rs27
-rw-r--r--src/tools/clippy/tests/ui/decimal_literal_representation.stderr46
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs123
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr89
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs55
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr50
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs150
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr75
-rw-r--r--src/tools/clippy/tests/ui/def_id_nocore.rs31
-rw-r--r--src/tools/clippy/tests/ui/def_id_nocore.stderr11
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed21
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs21
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr22
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed177
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs177
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr147
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed182
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs182
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr159
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.fixed99
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.rs99
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.stderr56
-rw-r--r--src/tools/clippy/tests/ui/default_union_representation.rs78
-rw-r--r--src/tools/clippy/tests/ui/default_union_representation.stderr48
-rw-r--r--src/tools/clippy/tests/ui/deprecated.rs22
-rw-r--r--src/tools/clippy/tests/ui/deprecated.stderr100
-rw-r--r--src/tools/clippy/tests/ui/deprecated_old.rs5
-rw-r--r--src/tools/clippy/tests/ui/deprecated_old.stderr22
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.fixed68
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.rs68
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.stderr74
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs23
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr22
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof_macro.rs10
-rw-r--r--src/tools/clippy/tests/ui/deref_by_slicing.fixed30
-rw-r--r--src/tools/clippy/tests/ui/deref_by_slicing.rs30
-rw-r--r--src/tools/clippy/tests/ui/deref_by_slicing.stderr58
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs243
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.stderr89
-rw-r--r--src/tools/clippy/tests/ui/derive.rs89
-rw-r--r--src/tools/clippy/tests/ui/derive.stderr103
-rw-r--r--src/tools/clippy/tests/ui/derive_hash_xor_eq.rs56
-rw-r--r--src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr59
-rw-r--r--src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs69
-rw-r--r--src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr63
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed126
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs126
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr70
-rw-r--r--src/tools/clippy/tests/ui/disallowed_script_idents.rs10
-rw-r--r--src/tools/clippy/tests/ui/disallowed_script_idents.stderr20
-rw-r--r--src/tools/clippy/tests/ui/diverging_sub_expression.rs41
-rw-r--r--src/tools/clippy/tests/ui/diverging_sub_expression.stderr48
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed215
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs215
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr333
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_1832.rs9
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_902.rs7
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs43
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr79
-rw-r--r--src/tools/clippy/tests/ui/doc_errors.rs104
-rw-r--r--src/tools/clippy/tests/ui/doc_errors.stderr58
-rw-r--r--src/tools/clippy/tests/ui/doc_link_with_quotes.rs12
-rw-r--r--src/tools/clippy/tests/ui/doc_link_with_quotes.stderr10
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.rs134
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.stderr55
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.fixed30
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.rs30
-rw-r--r--src/tools/clippy/tests/ui/double_comparison.stderr52
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.rs28
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.stderr27
-rw-r--r--src/tools/clippy/tests/ui/double_neg.rs8
-rw-r--r--src/tools/clippy/tests/ui/double_neg.stderr10
-rw-r--r--src/tools/clippy/tests/ui/double_parens.rs56
-rw-r--r--src/tools/clippy/tests/ui/double_parens.stderr40
-rw-r--r--src/tools/clippy/tests/ui/drop_forget_copy.rs66
-rw-r--r--src/tools/clippy/tests/ui/drop_forget_copy.stderr76
-rw-r--r--src/tools/clippy/tests/ui/drop_non_drop.rs40
-rw-r--r--src/tools/clippy/tests/ui/drop_non_drop.stderr27
-rw-r--r--src/tools/clippy/tests/ui/drop_ref.rs74
-rw-r--r--src/tools/clippy/tests/ui/drop_ref.stderr111
-rw-r--r--src/tools/clippy/tests/ui/duplicate_underscore_argument.rs10
-rw-r--r--src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr10
-rw-r--r--src/tools/clippy/tests/ui/duration_subsec.fixed29
-rw-r--r--src/tools/clippy/tests/ui/duration_subsec.rs29
-rw-r--r--src/tools/clippy/tests/ui/duration_subsec.stderr34
-rw-r--r--src/tools/clippy/tests/ui/else_if_without_else.rs58
-rw-r--r--src/tools/clippy/tests/ui/else_if_without_else.stderr27
-rw-r--r--src/tools/clippy/tests/ui/empty_drop.fixed24
-rw-r--r--src/tools/clippy/tests/ui/empty_drop.rs30
-rw-r--r--src/tools/clippy/tests/ui/empty_drop.stderr22
-rw-r--r--src/tools/clippy/tests/ui/empty_enum.rs7
-rw-r--r--src/tools/clippy/tests/ui/empty_enum.stderr11
-rw-r--r--src/tools/clippy/tests/ui/empty_enum_without_never_type.rs7
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs120
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr54
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.rs51
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.stderr27
-rw-r--r--src/tools/clippy/tests/ui/empty_loop_no_std.rs27
-rw-r--r--src/tools/clippy/tests/ui/empty_loop_no_std.stderr19
-rw-r--r--src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed25
-rw-r--r--src/tools/clippy/tests/ui/empty_structs_with_brackets.rs25
-rw-r--r--src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr19
-rw-r--r--src/tools/clippy/tests/ui/entry.fixed154
-rw-r--r--src/tools/clippy/tests/ui/entry.rs158
-rw-r--r--src/tools/clippy/tests/ui/entry.stderr217
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.fixed18
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.rs18
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.stderr20
-rw-r--r--src/tools/clippy/tests/ui/entry_with_else.fixed73
-rw-r--r--src/tools/clippy/tests/ui/entry_with_else.rs60
-rw-r--r--src/tools/clippy/tests/ui/entry_with_else.stderr151
-rw-r--r--src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs50
-rw-r--r--src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr58
-rw-r--r--src/tools/clippy/tests/ui/enum_glob_use.fixed30
-rw-r--r--src/tools/clippy/tests/ui/enum_glob_use.rs30
-rw-r--r--src/tools/clippy/tests/ui/enum_glob_use.stderr22
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.rs182
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.stderr149
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.rs49
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.stderr129
-rw-r--r--src/tools/clippy/tests/ui/eq_op.rs108
-rw-r--r--src/tools/clippy/tests/ui/eq_op.stderr172
-rw-r--r--src/tools/clippy/tests/ui/eq_op_macros.rs56
-rw-r--r--src/tools/clippy/tests/ui/eq_op_macros.stderr95
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.fixed84
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.rs84
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.stderr70
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.rs43
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.stderr34
-rw-r--r--src/tools/clippy/tests/ui/err_expect.fixed14
-rw-r--r--src/tools/clippy/tests/ui/err_expect.rs14
-rw-r--r--src/tools/clippy/tests/ui/err_expect.stderr10
-rw-r--r--src/tools/clippy/tests/ui/eta.fixed305
-rw-r--r--src/tools/clippy/tests/ui/eta.rs305
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr120
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.fixed69
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.rs69
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.stderr94
-rw-r--r--src/tools/clippy/tests/ui/exhaustive_items.fixed91
-rw-r--r--src/tools/clippy/tests/ui/exhaustive_items.rs88
-rw-r--r--src/tools/clippy/tests/ui/exhaustive_items.stderr61
-rw-r--r--src/tools/clippy/tests/ui/exit1.rs15
-rw-r--r--src/tools/clippy/tests/ui/exit1.stderr10
-rw-r--r--src/tools/clippy/tests/ui/exit2.rs13
-rw-r--r--src/tools/clippy/tests/ui/exit2.stderr10
-rw-r--r--src/tools/clippy/tests/ui/exit3.rs8
-rw-r--r--src/tools/clippy/tests/ui/expect.rs16
-rw-r--r--src/tools/clippy/tests/ui/expect.stderr19
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.fixed104
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.rs104
-rw-r--r--src/tools/clippy/tests/ui/expect_fun_call.stderr82
-rw-r--r--src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs142
-rw-r--r--src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr40
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.fixed218
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.rs218
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.stderr202
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.rs190
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.stderr60
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.fixed101
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.rs101
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.stderr76
-rw-r--r--src/tools/clippy/tests/ui/explicit_write.fixed63
-rw-r--r--src/tools/clippy/tests/ui/explicit_write.rs63
-rw-r--r--src/tools/clippy/tests/ui/explicit_write.stderr76
-rw-r--r--src/tools/clippy/tests/ui/extend_with_drain.fixed60
-rw-r--r--src/tools/clippy/tests/ui/extend_with_drain.rs60
-rw-r--r--src/tools/clippy/tests/ui/extend_with_drain.stderr28
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.rs129
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr40
-rw-r--r--src/tools/clippy/tests/ui/fallible_impl_from.rs76
-rw-r--r--src/tools/clippy/tests/ui/fallible_impl_from.stderr93
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.rs249
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.stderr135
-rw-r--r--src/tools/clippy/tests/ui/filetype_is_file.rs23
-rw-r--r--src/tools/clippy/tests/ui/filetype_is_file.stderr27
-rw-r--r--src/tools/clippy/tests/ui/filter_map_identity.fixed19
-rw-r--r--src/tools/clippy/tests/ui/filter_map_identity.rs19
-rw-r--r--src/tools/clippy/tests/ui/filter_map_identity.stderr28
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next.rs17
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next.stderr17
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.fixed10
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.rs10
-rw-r--r--src/tools/clippy/tests/ui/filter_map_next_fixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/find_map.rs33
-rw-r--r--src/tools/clippy/tests/ui/flat_map_identity.fixed17
-rw-r--r--src/tools/clippy/tests/ui/flat_map_identity.rs17
-rw-r--r--src/tools/clippy/tests/ui/flat_map_identity.stderr22
-rw-r--r--src/tools/clippy/tests/ui/flat_map_option.fixed13
-rw-r--r--src/tools/clippy/tests/ui/flat_map_option.rs13
-rw-r--r--src/tools/clippy/tests/ui/flat_map_option.stderr16
-rw-r--r--src/tools/clippy/tests/ui/float_arithmetic.rs52
-rw-r--r--src/tools/clippy/tests/ui/float_arithmetic.stderr106
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.rs115
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.stderr51
-rw-r--r--src/tools/clippy/tests/ui/float_cmp_const.rs58
-rw-r--r--src/tools/clippy/tests/ui/float_cmp_const.stderr67
-rw-r--r--src/tools/clippy/tests/ui/float_equality_without_abs.rs31
-rw-r--r--src/tools/clippy/tests/ui/float_equality_without_abs.stderr92
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.fixed84
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.rs84
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.stderr52
-rw-r--r--src/tools/clippy/tests/ui/floating_point_exp.fixed18
-rw-r--r--src/tools/clippy/tests/ui/floating_point_exp.rs18
-rw-r--r--src/tools/clippy/tests/ui/floating_point_exp.stderr28
-rw-r--r--src/tools/clippy/tests/ui/floating_point_hypot.fixed14
-rw-r--r--src/tools/clippy/tests/ui/floating_point_hypot.rs14
-rw-r--r--src/tools/clippy/tests/ui/floating_point_hypot.stderr22
-rw-r--r--src/tools/clippy/tests/ui/floating_point_log.fixed58
-rw-r--r--src/tools/clippy/tests/ui/floating_point_log.rs58
-rw-r--r--src/tools/clippy/tests/ui/floating_point_log.stderr174
-rw-r--r--src/tools/clippy/tests/ui/floating_point_logbase.fixed16
-rw-r--r--src/tools/clippy/tests/ui/floating_point_logbase.rs16
-rw-r--r--src/tools/clippy/tests/ui/floating_point_logbase.stderr28
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.fixed37
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.rs37
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.stderr64
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powf.fixed42
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powf.rs42
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powf.stderr150
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powi.fixed20
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powi.rs20
-rw-r--r--src/tools/clippy/tests/ui/floating_point_powi.stderr28
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.fixed25
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.rs25
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.stderr40
-rw-r--r--src/tools/clippy/tests/ui/fn_address_comparisons.rs20
-rw-r--r--src/tools/clippy/tests/ui/fn_address_comparisons.stderr16
-rw-r--r--src/tools/clippy/tests/ui/fn_params_excessive_bools.rs45
-rw-r--r--src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr53
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast.rs55
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr144
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs55
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr144
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs76
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr106
-rw-r--r--src/tools/clippy/tests/ui/for_kv_map.rs50
-rw-r--r--src/tools/clippy/tests/ui/for_kv_map.stderr58
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.fixed309
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.rs309
-rw-r--r--src/tools/clippy/tests/ui/for_loop_fixable.stderr96
-rw-r--r--src/tools/clippy/tests/ui/for_loop_unfixable.rs15
-rw-r--r--src/tools/clippy/tests/ui/for_loop_unfixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/for_loops_over_fallibles.rs72
-rw-r--r--src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr95
-rw-r--r--src/tools/clippy/tests/ui/forget_non_drop.rs27
-rw-r--r--src/tools/clippy/tests/ui/forget_non_drop.stderr27
-rw-r--r--src/tools/clippy/tests/ui/forget_ref.rs50
-rw-r--r--src/tools/clippy/tests/ui/forget_ref.stderr111
-rw-r--r--src/tools/clippy/tests/ui/format.fixed94
-rw-r--r--src/tools/clippy/tests/ui/format.rs96
-rw-r--r--src/tools/clippy/tests/ui/format.stderr127
-rw-r--r--src/tools/clippy/tests/ui/format_args.fixed117
-rw-r--r--src/tools/clippy/tests/ui/format_args.rs117
-rw-r--r--src/tools/clippy/tests/ui/format_args.stderr130
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.rs61
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.stderr175
-rw-r--r--src/tools/clippy/tests/ui/format_push_string.rs7
-rw-r--r--src/tools/clippy/tests/ui/format_push_string.stderr19
-rw-r--r--src/tools/clippy/tests/ui/formatting.rs73
-rw-r--r--src/tools/clippy/tests/ui/formatting.stderr52
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed61
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs61
-rw-r--r--src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr94
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.rs21
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.stderr11
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.rs52
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.stderr52
-rw-r--r--src/tools/clippy/tests/ui/functions.rs112
-rw-r--r--src/tools/clippy/tests/ui/functions.stderr108
-rw-r--r--src/tools/clippy/tests/ui/functions_maxlines.rs163
-rw-r--r--src/tools/clippy/tests/ui/functions_maxlines.stderr16
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.rs79
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.stderr145
-rw-r--r--src/tools/clippy/tests/ui/get_first.fixed42
-rw-r--r--src/tools/clippy/tests/ui/get_first.rs42
-rw-r--r--src/tools/clippy/tests/ui/get_first.stderr22
-rw-r--r--src/tools/clippy/tests/ui/get_last_with_len.fixed49
-rw-r--r--src/tools/clippy/tests/ui/get_last_with_len.rs49
-rw-r--r--src/tools/clippy/tests/ui/get_last_with_len.stderr40
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.fixed67
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.rs67
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.stderr191
-rw-r--r--src/tools/clippy/tests/ui/identity_op.fixed119
-rw-r--r--src/tools/clippy/tests/ui/identity_op.rs119
-rw-r--r--src/tools/clippy/tests/ui/identity_op.stderr238
-rw-r--r--src/tools/clippy/tests/ui/if_let_mutex.rs42
-rw-r--r--src/tools/clippy/tests/ui/if_let_mutex.stderr29
-rw-r--r--src/tools/clippy/tests/ui/if_not_else.rs29
-rw-r--r--src/tools/clippy/tests/ui/if_not_else.stderr27
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.rs217
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.stderr112
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else2.rs160
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else2.stderr125
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.rs115
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.stderr61
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.rs46
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.stderr39
-rw-r--r--src/tools/clippy/tests/ui/impl.rs67
-rw-r--r--src/tools/clippy/tests/ui/impl.stderr63
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.fixed118
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.rs118
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.stderr76
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.rs102
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.stderr164
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.fixed140
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.rs140
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.stderr109
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.fixed168
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.rs214
-rw-r--r--src/tools/clippy/tests/ui/implicit_saturating_sub.stderr188
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed47
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs47
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr70
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed73
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs77
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr20
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs166
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr158
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs28
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr22
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.rs48
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr64
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_slice.rs37
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_slice.stderr125
-rw-r--r--src/tools/clippy/tests/ui/inefficient_to_string.fixed31
-rw-r--r--src/tools/clippy/tests/ui/inefficient_to_string.rs31
-rw-r--r--src/tools/clippy/tests/ui/inefficient_to_string.stderr55
-rw-r--r--src/tools/clippy/tests/ui/infallible_destructuring_match.fixed112
-rw-r--r--src/tools/clippy/tests/ui/infallible_destructuring_match.rs118
-rw-r--r--src/tools/clippy/tests/ui/infallible_destructuring_match.stderr28
-rw-r--r--src/tools/clippy/tests/ui/infinite_iter.rs68
-rw-r--r--src/tools/clippy/tests/ui/infinite_iter.stderr109
-rw-r--r--src/tools/clippy/tests/ui/infinite_loop.rs217
-rw-r--r--src/tools/clippy/tests/ui/infinite_loop.stderr95
-rw-r--r--src/tools/clippy/tests/ui/inherent_to_string.rs106
-rw-r--r--src/tools/clippy/tests/ui/inherent_to_string.stderr28
-rw-r--r--src/tools/clippy/tests/ui/inline_fn_without_body.fixed17
-rw-r--r--src/tools/clippy/tests/ui/inline_fn_without_body.rs20
-rw-r--r--src/tools/clippy/tests/ui/inline_fn_without_body.stderr28
-rw-r--r--src/tools/clippy/tests/ui/inspect_for_each.rs22
-rw-r--r--src/tools/clippy/tests/ui/inspect_for_each.stderr16
-rw-r--r--src/tools/clippy/tests/ui/int_plus_one.fixed17
-rw-r--r--src/tools/clippy/tests/ui/int_plus_one.rs17
-rw-r--r--src/tools/clippy/tests/ui/int_plus_one.stderr28
-rw-r--r--src/tools/clippy/tests/ui/integer_arithmetic.rs102
-rw-r--r--src/tools/clippy/tests/ui/integer_arithmetic.stderr169
-rw-r--r--src/tools/clippy/tests/ui/integer_division.rs9
-rw-r--r--src/tools/clippy/tests/ui/integer_division.stderr27
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.fixed45
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.rs45
-rw-r--r--src/tools/clippy/tests/ui/into_iter_on_ref.stderr166
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed49
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs49
-rw-r--r--src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr154
-rw-r--r--src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs85
-rw-r--r--src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr166
-rw-r--r--src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs20
-rw-r--r--src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr22
-rw-r--r--src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed18
-rw-r--r--src/tools/clippy/tests/ui/is_digit_ascii_radix.rs18
-rw-r--r--src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr22
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.rs3
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.stderr8
-rw-r--r--src/tools/clippy/tests/ui/issue-7447.rs25
-rw-r--r--src/tools/clippy/tests/ui/issue-7447.stderr19
-rw-r--r--src/tools/clippy/tests/ui/issue_2356.fixed26
-rw-r--r--src/tools/clippy/tests/ui/issue_2356.rs26
-rw-r--r--src/tools/clippy/tests/ui/issue_2356.stderr14
-rw-r--r--src/tools/clippy/tests/ui/issue_4266.rs38
-rw-r--r--src/tools/clippy/tests/ui/issue_4266.stderr25
-rw-r--r--src/tools/clippy/tests/ui/item_after_statement.rs52
-rw-r--r--src/tools/clippy/tests/ui/item_after_statement.stderr33
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.fixed29
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.rs32
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.stderr38
-rw-r--r--src/tools/clippy/tests/ui/iter_count.fixed87
-rw-r--r--src/tools/clippy/tests/ui/iter_count.rs87
-rw-r--r--src/tools/clippy/tests/ui/iter_count.stderr154
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.fixed24
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.rs24
-rw-r--r--src/tools/clippy/tests/ui/iter_next_slice.stderr28
-rw-r--r--src/tools/clippy/tests/ui/iter_not_returning_iterator.rs74
-rw-r--r--src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr22
-rw-r--r--src/tools/clippy/tests/ui/iter_nth.rs56
-rw-r--r--src/tools/clippy/tests/ui/iter_nth.stderr59
-rw-r--r--src/tools/clippy/tests/ui/iter_nth_zero.fixed31
-rw-r--r--src/tools/clippy/tests/ui/iter_nth_zero.rs31
-rw-r--r--src/tools/clippy/tests/ui/iter_nth_zero.stderr22
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.fixed55
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.rs56
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.stderr70
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.fixed37
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.rs37
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.stderr46
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs19
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next_unfixable.stderr39
-rw-r--r--src/tools/clippy/tests/ui/iter_with_drain.fixed65
-rw-r--r--src/tools/clippy/tests/ui/iter_with_drain.rs65
-rw-r--r--src/tools/clippy/tests/ui/iter_with_drain.stderr40
-rw-r--r--src/tools/clippy/tests/ui/iterator_step_by_zero.rs28
-rw-r--r--src/tools/clippy/tests/ui/iterator_step_by_zero.stderr46
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.fixed37
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.rs37
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.stderr76
-rw-r--r--src/tools/clippy/tests/ui/large_digit_groups.fixed31
-rw-r--r--src/tools/clippy/tests/ui/large_digit_groups.rs31
-rw-r--r--src/tools/clippy/tests/ui/large_digit_groups.stderr48
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.rs135
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.stderr197
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.rs30
-rw-r--r--src/tools/clippy/tests/ui/large_stack_arrays.stderr35
-rw-r--r--src/tools/clippy/tests/ui/large_types_passed_by_value.rs66
-rw-r--r--src/tools/clippy/tests/ui/large_types_passed_by_value.stderr52
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.rs285
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.stderr123
-rw-r--r--src/tools/clippy/tests/ui/len_zero.fixed143
-rw-r--r--src/tools/clippy/tests/ui/len_zero.rs143
-rw-r--r--src/tools/clippy/tests/ui/len_zero.stderr88
-rw-r--r--src/tools/clippy/tests/ui/len_zero_ranges.fixed17
-rw-r--r--src/tools/clippy/tests/ui/len_zero_ranges.rs17
-rw-r--r--src/tools/clippy/tests/ui/len_zero_ranges.stderr16
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.rs169
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.stderr45
-rw-r--r--src/tools/clippy/tests/ui/let_if_seq.rs122
-rw-r--r--src/tools/clippy/tests/ui/let_if_seq.stderr50
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_drop.rs28
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_drop.stderr27
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.rs36
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.stderr83
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_must_use.rs95
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_must_use.stderr99
-rw-r--r--src/tools/clippy/tests/ui/let_unit.fixed177
-rw-r--r--src/tools/clippy/tests/ui/let_unit.rs177
-rw-r--r--src/tools/clippy/tests/ui/let_unit.stderr102
-rw-r--r--src/tools/clippy/tests/ui/linkedlist.rs48
-rw-r--r--src/tools/clippy/tests/ui/linkedlist.stderr75
-rw-r--r--src/tools/clippy/tests/ui/literals.rs42
-rw-r--r--src/tools/clippy/tests/ui/literals.stderr139
-rw-r--r--src/tools/clippy/tests/ui/logic_bug.rs34
-rw-r--r--src/tools/clippy/tests/ui/logic_bug.stderr63
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.fixed35
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.rs35
-rw-r--r--src/tools/clippy/tests/ui/lossy_float_literal.stderr70
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.fixed48
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.rs48
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr28
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports_expect.rs51
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.fixed52
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.stderr68
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.fixed52
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.stderr68
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.fixed45
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.rs68
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.fixed110
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.rs130
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.stderr165
-rw-r--r--src/tools/clippy/tests/ui/manual_bits.fixed59
-rw-r--r--src/tools/clippy/tests/ui/manual_bits.rs59
-rw-r--r--src/tools/clippy/tests/ui/manual_bits.stderr178
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.fixed121
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.rs134
-rw-r--r--src/tools/clippy/tests/ui/manual_filter_map.stderr194
-rw-r--r--src/tools/clippy/tests/ui/manual_find.rs22
-rw-r--r--src/tools/clippy/tests/ui/manual_find.stderr29
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.fixed182
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.rs242
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.stderr142
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.fixed124
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.rs137
-rw-r--r--src/tools/clippy/tests/ui/manual_find_map.stderr210
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.rs125
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.stderr199
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option.fixed157
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option.rs223
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option.stderr198
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.fixed60
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.rs75
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.stderr73
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.rs88
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr111
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs136
-rw-r--r--src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr115
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs87
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr41
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs74
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr65
-rw-r--r--src/tools/clippy/tests/ui/manual_ok_or.fixed40
-rw-r--r--src/tools/clippy/tests/ui/manual_ok_or.rs44
-rw-r--r--src/tools/clippy/tests/ui/manual_ok_or.stderr41
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed55
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs55
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr57
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.fixed240
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.rs246
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.stderr124
-rw-r--r--src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed45
-rw-r--r--src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs55
-rw-r--r--src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr163
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.fixed147
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.rs147
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.stderr213
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.fixed66
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.rs66
-rw-r--r--src/tools/clippy/tests/ui/manual_str_repeat.stderr64
-rw-r--r--src/tools/clippy/tests/ui/manual_strip.rs66
-rw-r--r--src/tools/clippy/tests/ui/manual_strip.stderr132
-rw-r--r--src/tools/clippy/tests/ui/manual_unwrap_or.fixed181
-rw-r--r--src/tools/clippy/tests/ui/manual_unwrap_or.rs223
-rw-r--r--src/tools/clippy/tests/ui/manual_unwrap_or.stderr155
-rw-r--r--src/tools/clippy/tests/ui/many_single_char_names.rs74
-rw-r--r--src/tools/clippy/tests/ui/many_single_char_names.stderr51
-rw-r--r--src/tools/clippy/tests/ui/map_clone.fixed63
-rw-r--r--src/tools/clippy/tests/ui/map_clone.rs63
-rw-r--r--src/tools/clippy/tests/ui/map_clone.stderr40
-rw-r--r--src/tools/clippy/tests/ui/map_collect_result_unit.fixed16
-rw-r--r--src/tools/clippy/tests/ui/map_collect_result_unit.rs16
-rw-r--r--src/tools/clippy/tests/ui/map_collect_result_unit.stderr16
-rw-r--r--src/tools/clippy/tests/ui/map_err.rs29
-rw-r--r--src/tools/clippy/tests/ui/map_err.stderr11
-rw-r--r--src/tools/clippy/tests/ui/map_flatten.rs55
-rw-r--r--src/tools/clippy/tests/ui/map_flatten.stderr100
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.fixed65
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.rs67
-rw-r--r--src/tools/clippy/tests/ui/map_flatten_fixable.stderr99
-rw-r--r--src/tools/clippy/tests/ui/map_identity.fixed25
-rw-r--r--src/tools/clippy/tests/ui/map_identity.rs27
-rw-r--r--src/tools/clippy/tests/ui/map_identity.stderr43
-rw-r--r--src/tools/clippy/tests/ui/map_unit_fn.rs11
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.rs81
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or.stderr150
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed54
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs58
-rw-r--r--src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr22
-rw-r--r--src/tools/clippy/tests/ui/match_as_ref.fixed43
-rw-r--r--src/tools/clippy/tests/ui/match_as_ref.rs52
-rw-r--r--src/tools/clippy/tests/ui/match_as_ref.stderr33
-rw-r--r--src/tools/clippy/tests/ui/match_bool.rs63
-rw-r--r--src/tools/clippy/tests/ui/match_bool.stderr117
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed170
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs211
-rw-r--r--src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr137
-rw-r--r--src/tools/clippy/tests/ui/match_on_vec_items.rs152
-rw-r--r--src/tools/clippy/tests/ui/match_on_vec_items.stderr52
-rw-r--r--src/tools/clippy/tests/ui/match_overlapping_arm.rs135
-rw-r--r--src/tools/clippy/tests/ui/match_overlapping_arm.stderr99
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.fixed118
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.rs118
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.stderr68
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.fixed63
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.rs63
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr36
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.rs56
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.stderr121
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.rs238
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.stderr196
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed126
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs142
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr200
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding2.fixed53
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding2.rs55
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding2.stderr68
-rw-r--r--src/tools/clippy/tests/ui/match_str_case_mismatch.fixed186
-rw-r--r--src/tools/clippy/tests/ui/match_str_case_mismatch.rs186
-rw-r--r--src/tools/clippy/tests/ui/match_str_case_mismatch.stderr80
-rw-r--r--src/tools/clippy/tests/ui/match_wild_err_arm.edition2018.stderr35
-rw-r--r--src/tools/clippy/tests/ui/match_wild_err_arm.edition2021.stderr35
-rw-r--r--src/tools/clippy/tests/ui/match_wild_err_arm.rs68
-rw-r--r--src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed134
-rw-r--r--src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs134
-rw-r--r--src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr52
-rw-r--r--src/tools/clippy/tests/ui/mem_forget.rs23
-rw-r--r--src/tools/clippy/tests/ui/mem_forget.stderr22
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.fixed79
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.rs79
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.stderr120
-rw-r--r--src/tools/clippy/tests/ui/mem_replace_macro.rs21
-rw-r--r--src/tools/clippy/tests/ui/mem_replace_macro.stderr14
-rw-r--r--src/tools/clippy/tests/ui/methods.rs140
-rw-r--r--src/tools/clippy/tests/ui/methods.stderr24
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.fixed11
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.rs11
-rw-r--r--src/tools/clippy/tests/ui/methods_fixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/min_max.rs62
-rw-r--r--src/tools/clippy/tests/ui/min_max.stderr82
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs228
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr37
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs4
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.rs11
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.stderr38
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_no_patch.rs14
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_outer_attr.rs4
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_outer_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed27
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs27
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr36
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed62
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_unix.rs62
-rw-r--r--src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr183
-rw-r--r--src/tools/clippy/tests/ui/mismatching_type_param_order.rs64
-rw-r--r--src/tools/clippy/tests/ui/mismatching_type_param_order.stderr83
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate-missing.rs3
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr12
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate.rs4
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-impl.rs92
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-impl.stderr107
-rw-r--r--src/tools/clippy/tests/ui/missing-doc.rs102
-rw-r--r--src/tools/clippy/tests/ui/missing-doc.stderr159
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs8
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs121
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs81
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr85
-rw-r--r--src/tools/clippy/tests/ui/missing_inline.rs66
-rw-r--r--src/tools/clippy/tests/ui/missing_inline.stderr40
-rw-r--r--src/tools/clippy/tests/ui/missing_inline_executable.rs5
-rw-r--r--src/tools/clippy/tests/ui/missing_inline_proc_macro.rs23
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.rs153
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.stderr108
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop.fixed28
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop.rs28
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop.stderr40
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop_no_std.fixed23
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop_no_std.rs23
-rw-r--r--src/tools/clippy/tests/ui/missing_spin_loop_no_std.stderr10
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed43
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.rs43
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr100
-rw-r--r--src/tools/clippy/tests/ui/mixed_read_write_in_expression.rs112
-rw-r--r--src/tools/clippy/tests/ui/mixed_read_write_in_expression.stderr51
-rw-r--r--src/tools/clippy/tests/ui/module_inception.rs21
-rw-r--r--src/tools/clippy/tests/ui/module_inception.stderr20
-rw-r--r--src/tools/clippy/tests/ui/module_name_repetitions.rs18
-rw-r--r--src/tools/clippy/tests/ui/module_name_repetitions.stderr34
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_float.rs29
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr83
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs83
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr156
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs42
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr156
-rw-r--r--src/tools/clippy/tests/ui/modulo_one.rs23
-rw-r--r--src/tools/clippy/tests/ui/modulo_one.stderr60
-rw-r--r--src/tools/clippy/tests/ui/must_use_candidates.fixed93
-rw-r--r--src/tools/clippy/tests/ui/must_use_candidates.rs93
-rw-r--r--src/tools/clippy/tests/ui/must_use_candidates.stderr34
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.fixed26
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.rs26
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.stderr28
-rw-r--r--src/tools/clippy/tests/ui/mut_from_ref.rs54
-rw-r--r--src/tools/clippy/tests/ui/mut_from_ref.stderr75
-rw-r--r--src/tools/clippy/tests/ui/mut_key.rs85
-rw-r--r--src/tools/clippy/tests/ui/mut_key.stderr106
-rw-r--r--src/tools/clippy/tests/ui/mut_mut.rs59
-rw-r--r--src/tools/clippy/tests/ui/mut_mut.stderr63
-rw-r--r--src/tools/clippy/tests/ui/mut_mutex_lock.fixed21
-rw-r--r--src/tools/clippy/tests/ui/mut_mutex_lock.rs21
-rw-r--r--src/tools/clippy/tests/ui/mut_mutex_lock.stderr10
-rw-r--r--src/tools/clippy/tests/ui/mut_range_bound.rs84
-rw-r--r--src/tools/clippy/tests/ui/mut_range_bound.stderr59
-rw-r--r--src/tools/clippy/tests/ui/mut_reference.rs43
-rw-r--r--src/tools/clippy/tests/ui/mut_reference.stderr22
-rw-r--r--src/tools/clippy/tests/ui/mutex_atomic.rs17
-rw-r--r--src/tools/clippy/tests/ui/mutex_atomic.stderr48
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed69
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs69
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr40
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs46
-rw-r--r--src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_bitwise_bool.fixed40
-rw-r--r--src/tools/clippy/tests/ui/needless_bitwise_bool.rs40
-rw-r--r--src/tools/clippy/tests/ui/needless_bitwise_bool.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.fixed126
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.rs186
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.stderr193
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/simple.rs47
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/simple.stderr44
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed185
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs185
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr136
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow_pat.rs150
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow_pat.stderr112
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.fixed45
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.rs45
-rw-r--r--src/tools/clippy/tests/ui/needless_borrowed_ref.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_collect.fixed36
-rw-r--r--src/tools/clippy/tests/ui/needless_collect.rs36
-rw-r--r--src/tools/clippy/tests/ui/needless_collect.stderr70
-rw-r--r--src/tools/clippy/tests/ui/needless_collect_indirect.rs114
-rw-r--r--src/tools/clippy/tests/ui/needless_collect_indirect.stderr129
-rw-r--r--src/tools/clippy/tests/ui/needless_continue.rs144
-rw-r--r--src/tools/clippy/tests/ui/needless_continue.stderr131
-rw-r--r--src/tools/clippy/tests/ui/needless_doc_main.rs140
-rw-r--r--src/tools/clippy/tests/ui/needless_doc_main.stderr28
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_fixable.fixed118
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_fixable.rs118
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_fixable.stderr123
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_unfixable.rs14
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr30
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.fixed273
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs273
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.stderr274
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.rs422
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.stderr190
-rw-r--r--src/tools/clippy/tests/ui/needless_match.fixed210
-rw-r--r--src/tools/clippy/tests/ui/needless_match.rs247
-rw-r--r--src/tools/clippy/tests/ui/needless_match.stderr113
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.fixed55
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.rs55
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.stderr22
-rw-r--r--src/tools/clippy/tests/ui/needless_option_take.fixed15
-rw-r--r--src/tools/clippy/tests/ui/needless_option_take.rs15
-rw-r--r--src/tools/clippy/tests/ui/needless_option_take.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_parens_on_range_literals.fixed14
-rw-r--r--src/tools/clippy/tests/ui/needless_parens_on_range_literals.rs14
-rw-r--r--src/tools/clippy/tests/ui/needless_parens_on_range_literals.stderr40
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_value.rs160
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_value.stderr178
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs21
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.fixed140
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.rs140
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.stderr93
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.rs95
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.stderr157
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop2.rs109
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop2.stderr91
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed240
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs240
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr226
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.fixed47
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.rs47
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.stderr82
-rw-r--r--src/tools/clippy/tests/ui/needless_update.rs25
-rw-r--r--src/tools/clippy/tests/ui/needless_update.stderr10
-rw-r--r--src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs62
-rw-r--r--src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr28
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.fixed48
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.rs48
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.stderr52
-rw-r--r--src/tools/clippy/tests/ui/never_loop.rs221
-rw-r--r--src/tools/clippy/tests/ui/never_loop.stderr105
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs352
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.stderr80
-rw-r--r--src/tools/clippy/tests/ui/new_without_default.rs228
-rw-r--r--src/tools/clippy/tests/ui/new_without_default.stderr124
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs143
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr186
-rw-r--r--src/tools/clippy/tests/ui/no_effect_replace.rs51
-rw-r--r--src/tools/clippy/tests/ui/no_effect_replace.stderr52
-rw-r--r--src/tools/clippy/tests/ui/non_expressive_names.rs58
-rw-r--r--src/tools/clippy/tests/ui/non_expressive_names.stderr40
-rw-r--r--src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed33
-rw-r--r--src/tools/clippy/tests/ui/non_octal_unix_permissions.rs33
-rw-r--r--src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr28
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs133
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr171
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs59
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr111
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed111
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.rs111
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr82
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.fixed39
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.rs47
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.stderr26
-rw-r--r--src/tools/clippy/tests/ui/obfuscated_if_else.fixed7
-rw-r--r--src/tools/clippy/tests/ui/obfuscated_if_else.rs7
-rw-r--r--src/tools/clippy/tests/ui/obfuscated_if_else.stderr10
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.rs20
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.stderr131
-rw-r--r--src/tools/clippy/tests/ui/ok_expect.rs27
-rw-r--r--src/tools/clippy/tests/ui/ok_expect.stderr43
-rw-r--r--src/tools/clippy/tests/ui/only_used_in_recursion.rs122
-rw-r--r--src/tools/clippy/tests/ui/only_used_in_recursion.stderr82
-rw-r--r--src/tools/clippy/tests/ui/op_ref.rs94
-rw-r--r--src/tools/clippy/tests/ui/op_ref.stderr38
-rw-r--r--src/tools/clippy/tests/ui/open_options.rs14
-rw-r--r--src/tools/clippy/tests/ui/open_options.stderr46
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.fixed44
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.rs47
-rw-r--r--src/tools/clippy/tests/ui/option_as_ref_deref.stderr110
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.rs24
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.stderr61
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.fixed25
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.rs27
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.stderr56
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed182
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs211
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr210
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.fixed26
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.rs28
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.stderr53
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed88
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs88
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr156
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs39
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr27
-rw-r--r--src/tools/clippy/tests/ui/option_option.rs89
-rw-r--r--src/tools/clippy/tests/ui/option_option.stderr80
-rw-r--r--src/tools/clippy/tests/ui/option_take_on_temporary.fixed15
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed229
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs229
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr136
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.fixed52
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.rs52
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.stderr22
-rw-r--r--src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs11
-rw-r--r--src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr16
-rw-r--r--src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs22
-rw-r--r--src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr40
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.rs25
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.stderr52
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn.rs70
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn.stderr99
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs48
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr54
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs43
-rw-r--r--src/tools/clippy/tests/ui/panicking_macros.rs95
-rw-r--r--src/tools/clippy/tests/ui/panicking_macros.stderr106
-rw-r--r--src/tools/clippy/tests/ui/partialeq_ne_impl.rs26
-rw-r--r--src/tools/clippy/tests/ui/partialeq_ne_impl.stderr12
-rw-r--r--src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed8
-rw-r--r--src/tools/clippy/tests/ui/path_buf_push_overwrite.rs8
-rw-r--r--src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr10
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs49
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr19
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs24
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr27
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs45
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr67
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs57
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr83
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs146
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr79
-rw-r--r--src/tools/clippy/tests/ui/patterns.fixed36
-rw-r--r--src/tools/clippy/tests/ui/patterns.rs36
-rw-r--r--src/tools/clippy/tests/ui/patterns.stderr22
-rw-r--r--src/tools/clippy/tests/ui/precedence.fixed61
-rw-r--r--src/tools/clippy/tests/ui/precedence.rs61
-rw-r--r--src/tools/clippy/tests/ui/precedence.stderr76
-rw-r--r--src/tools/clippy/tests/ui/print.rs35
-rw-r--r--src/tools/clippy/tests/ui/print.stderr54
-rw-r--r--src/tools/clippy/tests/ui/print_in_format_impl.rs58
-rw-r--r--src/tools/clippy/tests/ui/print_in_format_impl.stderr46
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs38
-rw-r--r--src/tools/clippy/tests/ui/print_literal.stderr135
-rw-r--r--src/tools/clippy/tests/ui/print_stderr.rs8
-rw-r--r--src/tools/clippy/tests/ui/print_stderr.stderr16
-rw-r--r--src/tools/clippy/tests/ui/print_stdout_build_script.rs12
-rw-r--r--src/tools/clippy/tests/ui/print_with_newline.rs52
-rw-r--r--src/tools/clippy/tests/ui/print_with_newline.stderr129
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.fixed18
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.rs18
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.stderr28
-rw-r--r--src/tools/clippy/tests/ui/proc_macro.rs26
-rw-r--r--src/tools/clippy/tests/ui/proc_macro.stderr11
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs209
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr166
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed65
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs65
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr57
-rw-r--r--src/tools/clippy/tests/ui/ptr_eq.fixed38
-rw-r--r--src/tools/clippy/tests/ui/ptr_eq.rs38
-rw-r--r--src/tools/clippy/tests/ui/ptr_eq.stderr16
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed20
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.rs20
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr16
-rw-r--r--src/tools/clippy/tests/ui/pub_use.rs14
-rw-r--r--src/tools/clippy/tests/ui/pub_use.stderr11
-rw-r--r--src/tools/clippy/tests/ui/question_mark.fixed210
-rw-r--r--src/tools/clippy/tests/ui/question_mark.rs246
-rw-r--r--src/tools/clippy/tests/ui/question_mark.stderr134
-rw-r--r--src/tools/clippy/tests/ui/range.rs16
-rw-r--r--src/tools/clippy/tests/ui/range.stderr10
-rw-r--r--src/tools/clippy/tests/ui/range_contains.fixed64
-rw-r--r--src/tools/clippy/tests/ui/range_contains.rs64
-rw-r--r--src/tools/clippy/tests/ui/range_contains.stderr124
-rw-r--r--src/tools/clippy/tests/ui/range_plus_minus_one.fixed42
-rw-r--r--src/tools/clippy/tests/ui/range_plus_minus_one.rs42
-rw-r--r--src/tools/clippy/tests/ui/range_plus_minus_one.stderr60
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer.fixed28
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer.rs28
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer.stderr52
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer_arc.fixed27
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer_arc.rs27
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer_arc.stderr52
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs12
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs68
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr109
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs69
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr109
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs83
-rw-r--r--src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr201
-rw-r--r--src/tools/clippy/tests/ui/rc_mutex.rs36
-rw-r--r--src/tools/clippy/tests/ui/rc_mutex.stderr35
-rw-r--r--src/tools/clippy/tests/ui/read_zero_byte_vec.rs87
-rw-r--r--src/tools/clippy/tests/ui/read_zero_byte_vec.stderr64
-rw-r--r--src/tools/clippy/tests/ui/recursive_format_impl.rs322
-rw-r--r--src/tools/clippy/tests/ui/recursive_format_impl.stderr82
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation.rs135
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation.stderr183
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed75
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation_fixable.rs75
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr99
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.fixed241
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.rs241
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.stderr183
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_early.rs20
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_early.stderr16
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed8
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs8
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr10
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_late.rs40
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_late.stderr22
-rw-r--r--src/tools/clippy/tests/ui/redundant_else.rs154
-rw-r--r--src/tools/clippy/tests/ui/redundant_else.stderr80
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.fixed71
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.rs71
-rw-r--r--src/tools/clippy/tests/ui/redundant_field_names.stderr46
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed58
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs58
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr171
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed73
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs91
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr130
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed88
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs103
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr146
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed76
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs91
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr128
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed110
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs128
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr154
-rw-r--r--src/tools/clippy/tests/ui/redundant_pub_crate.fixed117
-rw-r--r--src/tools/clippy/tests/ui/redundant_pub_crate.rs117
-rw-r--r--src/tools/clippy/tests/ui/redundant_pub_crate.stderr132
-rw-r--r--src/tools/clippy/tests/ui/redundant_slicing.fixed46
-rw-r--r--src/tools/clippy/tests/ui/redundant_slicing.rs46
-rw-r--r--src/tools/clippy/tests/ui/redundant_slicing.stderr22
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed56
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.rs56
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr100
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs13
-rw-r--r--src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr64
-rw-r--r--src/tools/clippy/tests/ui/ref_binding_to_reference.rs85
-rw-r--r--src/tools/clippy/tests/ui/ref_binding_to_reference.stderr88
-rw-r--r--src/tools/clippy/tests/ui/ref_option_ref.rs47
-rw-r--r--src/tools/clippy/tests/ui/ref_option_ref.stderr70
-rw-r--r--src/tools/clippy/tests/ui/regex.rs82
-rw-r--r--src/tools/clippy/tests/ui/regex.stderr171
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed72
-rw-r--r--src/tools/clippy/tests/ui/rename.rs72
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr214
-rw-r--r--src/tools/clippy/tests/ui/renamed_builtin_attr.fixed4
-rw-r--r--src/tools/clippy/tests/ui/renamed_builtin_attr.rs4
-rw-r--r--src/tools/clippy/tests/ui/renamed_builtin_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/repeat_once.fixed16
-rw-r--r--src/tools/clippy/tests/ui/repeat_once.rs16
-rw-r--r--src/tools/clippy/tests/ui/repeat_once.stderr40
-rw-r--r--src/tools/clippy/tests/ui/repl_uninit.rs41
-rw-r--r--src/tools/clippy/tests/ui/repl_uninit.stderr30
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs57
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr27
-rw-r--r--src/tools/clippy/tests/ui/result_map_or_into_option.fixed19
-rw-r--r--src/tools/clippy/tests/ui/result_map_or_into_option.rs19
-rw-r--r--src/tools/clippy/tests/ui/result_map_or_into_option.stderr10
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed82
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs82
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr148
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs46
-rw-r--r--src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr58
-rw-r--r--src/tools/clippy/tests/ui/result_unit_error.rs56
-rw-r--r--src/tools/clippy/tests/ui/result_unit_error.stderr43
-rw-r--r--src/tools/clippy/tests/ui/return_self_not_must_use.rs58
-rw-r--r--src/tools/clippy/tests/ui/return_self_not_must_use.stderr31
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed29
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs29
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr47
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed57
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs57
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr69
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs11
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr16
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs15
-rw-r--r--src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr22
-rw-r--r--src/tools/clippy/tests/ui/same_functions_in_if_condition.rs109
-rw-r--r--src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr75
-rw-r--r--src/tools/clippy/tests/ui/same_item_push.rs158
-rw-r--r--src/tools/clippy/tests/ui/same_item_push.stderr43
-rw-r--r--src/tools/clippy/tests/ui/same_name_method.rs127
-rw-r--r--src/tools/clippy/tests/ui/same_name_method.stderr64
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.rs79
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.stderr87
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed216
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.rs222
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr285
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed248
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.rs251
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr292
-rw-r--r--src/tools/clippy/tests/ui/self_assignment.rs67
-rw-r--r--src/tools/clippy/tests/ui/self_assignment.stderr70
-rw-r--r--src/tools/clippy/tests/ui/self_named_constructors.rs59
-rw-r--r--src/tools/clippy/tests/ui/self_named_constructors.stderr12
-rw-r--r--src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs122
-rw-r--r--src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr34
-rw-r--r--src/tools/clippy/tests/ui/serde.rs47
-rw-r--r--src/tools/clippy/tests/ui/serde.stderr15
-rw-r--r--src/tools/clippy/tests/ui/shadow.rs98
-rw-r--r--src/tools/clippy/tests/ui/shadow.stderr281
-rw-r--r--src/tools/clippy/tests/ui/short_circuit_statement.fixed18
-rw-r--r--src/tools/clippy/tests/ui/short_circuit_statement.rs18
-rw-r--r--src/tools/clippy/tests/ui/short_circuit_statement.stderr22
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs84
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs87
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr143
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs88
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr153
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs630
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr497
-rw-r--r--src/tools/clippy/tests/ui/similar_names.rs121
-rw-r--r--src/tools/clippy/tests/ui/similar_names.stderr87
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.fixed45
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.rs45
-rw-r--r--src/tools/clippy/tests/ui/single_char_add_str.stderr94
-rw-r--r--src/tools/clippy/tests/ui/single_char_lifetime_names.rs44
-rw-r--r--src/tools/clippy/tests/ui/single_char_lifetime_names.stderr43
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.fixed67
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.rs67
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.stderr238
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.fixed33
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.rs33
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.stderr16
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_macro.rs20
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs16
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr25
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs15
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs16
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.fixed36
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.rs30
-rw-r--r--src/tools/clippy/tests/ui/single_element_loop.stderr99
-rw-r--r--src/tools/clippy/tests/ui/single_match.rs245
-rw-r--r--src/tools/clippy/tests/ui/single_match.stderr159
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.rs119
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.stderr104
-rw-r--r--src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs37
-rw-r--r--src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr35
-rw-r--r--src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs46
-rw-r--r--src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr171
-rw-r--r--src/tools/clippy/tests/ui/skip_while_next.rs29
-rw-r--r--src/tools/clippy/tests/ui/skip_while_next.stderr23
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.rs69
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.stderr76
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.fixed32
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.rs32
-rw-r--r--src/tools/clippy/tests/ui/stable_sort_primitive.stderr59
-rw-r--r--src/tools/clippy/tests/ui/starts_ends_with.fixed54
-rw-r--r--src/tools/clippy/tests/ui/starts_ends_with.rs54
-rw-r--r--src/tools/clippy/tests/ui/starts_ends_with.stderr102
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs45
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.stderr93
-rw-r--r--src/tools/clippy/tests/ui/str_to_string.rs7
-rw-r--r--src/tools/clippy/tests/ui/str_to_string.stderr19
-rw-r--r--src/tools/clippy/tests/ui/string_add.rs26
-rw-r--r--src/tools/clippy/tests/ui/string_add.stderr30
-rw-r--r--src/tools/clippy/tests/ui/string_add_assign.fixed21
-rw-r--r--src/tools/clippy/tests/ui/string_add_assign.rs21
-rw-r--r--src/tools/clippy/tests/ui/string_add_assign.stderr24
-rw-r--r--src/tools/clippy/tests/ui/string_extend.fixed32
-rw-r--r--src/tools/clippy/tests/ui/string_extend.rs32
-rw-r--r--src/tools/clippy/tests/ui/string_extend.stderr22
-rw-r--r--src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed6
-rw-r--r--src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs6
-rw-r--r--src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr10
-rw-r--r--src/tools/clippy/tests/ui/string_lit_as_bytes.fixed30
-rw-r--r--src/tools/clippy/tests/ui/string_lit_as_bytes.rs30
-rw-r--r--src/tools/clippy/tests/ui/string_lit_as_bytes.stderr40
-rw-r--r--src/tools/clippy/tests/ui/string_slice.rs10
-rw-r--r--src/tools/clippy/tests/ui/string_slice.stderr22
-rw-r--r--src/tools/clippy/tests/ui/string_to_string.rs7
-rw-r--r--src/tools/clippy/tests/ui/string_to_string.stderr11
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.fixed34
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.rs34
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.stderr46
-rw-r--r--src/tools/clippy/tests/ui/struct_excessive_bools.rs44
-rw-r--r--src/tools/clippy/tests/ui/struct_excessive_bools.stderr29
-rw-r--r--src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs170
-rw-r--r--src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr60
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.rs115
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.stderr90
-rw-r--r--src/tools/clippy/tests/ui/suspicious_map.rs32
-rw-r--r--src/tools/clippy/tests/ui/suspicious_map.stderr19
-rw-r--r--src/tools/clippy/tests/ui/suspicious_operation_groupings.fixed209
-rw-r--r--src/tools/clippy/tests/ui/suspicious_operation_groupings.rs209
-rw-r--r--src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr160
-rw-r--r--src/tools/clippy/tests/ui/suspicious_splitn.rs21
-rw-r--r--src/tools/clippy/tests/ui/suspicious_splitn.stderr75
-rw-r--r--src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs23
-rw-r--r--src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr35
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed157
-rw-r--r--src/tools/clippy/tests/ui/swap.rs181
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr122
-rw-r--r--src/tools/clippy/tests/ui/swap_ptr_to_ref.fixed24
-rw-r--r--src/tools/clippy/tests/ui/swap_ptr_to_ref.rs24
-rw-r--r--src/tools/clippy/tests/ui/swap_ptr_to_ref.stderr28
-rw-r--r--src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.rs18
-rw-r--r--src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.stderr22
-rw-r--r--src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed22
-rw-r--r--src/tools/clippy/tests/ui/tabs_in_doc_comments.rs22
-rw-r--r--src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr52
-rw-r--r--src/tools/clippy/tests/ui/temporary_assignment.rs71
-rw-r--r--src/tools/clippy/tests/ui/temporary_assignment.stderr32
-rw-r--r--src/tools/clippy/tests/ui/to_digit_is_some.fixed11
-rw-r--r--src/tools/clippy/tests/ui/to_digit_is_some.rs11
-rw-r--r--src/tools/clippy/tests/ui/to_digit_is_some.stderr16
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.fixed50
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.rs50
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.stderr45
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs33
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr21
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.rs185
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.stderr120
-rw-r--r--src/tools/clippy/tests/ui/trailing_zeros.rs10
-rw-r--r--src/tools/clippy/tests/ui/trailing_zeros.stderr16
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs212
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr167
-rw-r--r--src/tools/clippy/tests/ui/transmute.rs162
-rw-r--r--src/tools/clippy/tests/ui/transmute.stderr244
-rw-r--r--src/tools/clippy/tests/ui/transmute_32bit.rs14
-rw-r--r--src/tools/clippy/tests/ui/transmute_32bit.stderr28
-rw-r--r--src/tools/clippy/tests/ui/transmute_64bit.rs10
-rw-r--r--src/tools/clippy/tests/ui/transmute_64bit.stderr16
-rw-r--r--src/tools/clippy/tests/ui/transmute_collection.rs50
-rw-r--r--src/tools/clippy/tests/ui/transmute_collection.stderr112
-rw-r--r--src/tools/clippy/tests/ui/transmute_float_to_int.rs25
-rw-r--r--src/tools/clippy/tests/ui/transmute_float_to_int.stderr40
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs63
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr40
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed78
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs78
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr136
-rw-r--r--src/tools/clippy/tests/ui/transmute_undefined_repr.rs144
-rw-r--r--src/tools/clippy/tests/ui/transmute_undefined_repr.stderr80
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed77
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs77
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr56
-rw-r--r--src/tools/clippy/tests/ui/transmuting_null.rs30
-rw-r--r--src/tools/clippy/tests/ui/transmuting_null.stderr22
-rw-r--r--src/tools/clippy/tests/ui/trim_split_whitespace.fixed91
-rw-r--r--src/tools/clippy/tests/ui/trim_split_whitespace.rs91
-rw-r--r--src/tools/clippy/tests/ui/trim_split_whitespace.stderr52
-rw-r--r--src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs168
-rw-r--r--src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr116
-rw-r--r--src/tools/clippy/tests/ui/try_err.fixed170
-rw-r--r--src/tools/clippy/tests/ui/try_err.rs170
-rw-r--r--src/tools/clippy/tests/ui/try_err.stderr84
-rw-r--r--src/tools/clippy/tests/ui/ty_fn_sig.rs14
-rw-r--r--src/tools/clippy/tests/ui/type_complexity.rs69
-rw-r--r--src/tools/clippy/tests/ui/type_complexity.stderr94
-rw-r--r--src/tools/clippy/tests/ui/type_repetition_in_bounds.rs97
-rw-r--r--src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr39
-rw-r--r--src/tools/clippy/tests/ui/types.fixed15
-rw-r--r--src/tools/clippy/tests/ui/types.rs15
-rw-r--r--src/tools/clippy/tests/ui/types.stderr10
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs493
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr267
-rw-r--r--src/tools/clippy/tests/ui/undropped_manually_drops.rs26
-rw-r--r--src/tools/clippy/tests/ui/undropped_manually_drops.stderr19
-rw-r--r--src/tools/clippy/tests/ui/unicode.fixed36
-rw-r--r--src/tools/clippy/tests/ui/unicode.rs36
-rw-r--r--src/tools/clippy/tests/ui/unicode.stderr50
-rw-r--r--src/tools/clippy/tests/ui/uninit.rs26
-rw-r--r--src/tools/clippy/tests/ui/uninit.stderr22
-rw-r--r--src/tools/clippy/tests/ui/uninit_vec.rs94
-rw-r--r--src/tools/clippy/tests/ui/uninit_vec.stderr105
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.rs133
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.stderr187
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed30
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs27
-rw-r--r--src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr45
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.rs61
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.stderr74
-rw-r--r--src/tools/clippy/tests/ui/unit_hash.rs28
-rw-r--r--src/tools/clippy/tests/ui/unit_hash.stderr27
-rw-r--r--src/tools/clippy/tests/ui/unit_return_expecting_ord.rs36
-rw-r--r--src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr39
-rw-r--r--src/tools/clippy/tests/ui/unknown_attribute.rs3
-rw-r--r--src/tools/clippy/tests/ui/unknown_attribute.stderr8
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.fixed18
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.rs18
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.stderr52
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.fixed91
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.rs91
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_cast.stderr154
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_clone.rs110
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_clone.stderr106
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_filter_map.rs150
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_filter_map.stderr38
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_find_map.rs23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_find_map.stderr38
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.fixed52
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.rs52
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.stderr40
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed142
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs142
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr35
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.fixed35
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.rs37
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_join.stderr20
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed132
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs132
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr283
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs22
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr28
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed79
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs83
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr128
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed22
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs22
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr16
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_self_imports.fixed10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_self_imports.rs10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_self_imports.stderr23
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_sort_by.fixed103
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_sort_by.rs103
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_sort_by.stderr76
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed331
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs331
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr513
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_wraps.rs144
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_wraps.stderr156
-rw-r--r--src/tools/clippy/tests/ui/unneeded_field_pattern.rs22
-rw-r--r--src/tools/clippy/tests/ui/unneeded_field_pattern.stderr19
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed45
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs45
-rw-r--r--src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr92
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.fixed35
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.rs35
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.stderr179
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.fixed17
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.rs17
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns2.stderr91
-rw-r--r--src/tools/clippy/tests/ui/unreadable_literal.fixed46
-rw-r--r--src/tools/clippy/tests/ui/unreadable_literal.rs46
-rw-r--r--src/tools/clippy/tests/ui/unreadable_literal.stderr72
-rw-r--r--src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs70
-rw-r--r--src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr39
-rw-r--r--src/tools/clippy/tests/ui/unsafe_removed_from_name.rs27
-rw-r--r--src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr22
-rw-r--r--src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed42
-rw-r--r--src/tools/clippy/tests/ui/unseparated_prefix_literals.rs42
-rw-r--r--src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr63
-rw-r--r--src/tools/clippy/tests/ui/unused_async.rs48
-rw-r--r--src/tools/clippy/tests/ui/unused_async.stderr23
-rw-r--r--src/tools/clippy/tests/ui/unused_io_amount.rs117
-rw-r--r--src/tools/clippy/tests/ui/unused_io_amount.stderr131
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.fixed9
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.rs9
-rw-r--r--src/tools/clippy/tests/ui/unused_rounding.stderr22
-rw-r--r--src/tools/clippy/tests/ui/unused_self.rs149
-rw-r--r--src/tools/clippy/tests/ui/unused_self.stderr75
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.fixed89
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.rs89
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.stderr122
-rw-r--r--src/tools/clippy/tests/ui/unwrap.rs16
-rw-r--r--src/tools/clippy/tests/ui/unwrap.stderr19
-rw-r--r--src/tools/clippy/tests/ui/unwrap_in_result.rs44
-rw-r--r--src/tools/clippy/tests/ui/unwrap_in_result.stderr41
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.rs9
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or.stderr16
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or_else_default.fixed74
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or_else_default.rs74
-rw-r--r--src/tools/clippy/tests/ui/unwrap_or_else_default.stderr34
-rwxr-xr-xsrc/tools/clippy/tests/ui/update-all-references.sh3
-rw-r--r--src/tools/clippy/tests/ui/upper_case_acronyms.rs41
-rw-r--r--src/tools/clippy/tests/ui/upper_case_acronyms.stderr58
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed610
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs610
-rw-r--r--src/tools/clippy/tests/ui/use_self.stderr250
-rw-r--r--src/tools/clippy/tests/ui/use_self_trait.fixed115
-rw-r--r--src/tools/clippy/tests/ui/use_self_trait.rs115
-rw-r--r--src/tools/clippy/tests/ui/use_self_trait.stderr88
-rw-r--r--src/tools/clippy/tests/ui/used_underscore_binding.rs124
-rw-r--r--src/tools/clippy/tests/ui/used_underscore_binding.stderr40
-rw-r--r--src/tools/clippy/tests/ui/useful_asref.rs13
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.fixed136
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.rs136
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.stderr74
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.fixed75
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.rs75
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.stderr22
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.fixed92
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.rs92
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion.stderr92
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion_try.rs40
-rw-r--r--src/tools/clippy/tests/ui/useless_conversion_try.stderr79
-rw-r--r--src/tools/clippy/tests/ui/vec.fixed78
-rw-r--r--src/tools/clippy/tests/ui/vec.rs78
-rw-r--r--src/tools/clippy/tests/ui/vec.stderr70
-rw-r--r--src/tools/clippy/tests/ui/vec_box_sized.fixed54
-rw-r--r--src/tools/clippy/tests/ui/vec_box_sized.rs54
-rw-r--r--src/tools/clippy/tests/ui/vec_box_sized.stderr40
-rw-r--r--src/tools/clippy/tests/ui/vec_init_then_push.rs112
-rw-r--r--src/tools/clippy/tests/ui/vec_init_then_push.stderr73
-rw-r--r--src/tools/clippy/tests/ui/vec_resize_to_zero.rs15
-rw-r--r--src/tools/clippy/tests/ui/vec_resize_to_zero.stderr13
-rw-r--r--src/tools/clippy/tests/ui/verbose_file_reads.rs28
-rw-r--r--src/tools/clippy/tests/ui/verbose_file_reads.stderr19
-rw-r--r--src/tools/clippy/tests/ui/vtable_address_comparisons.rs44
-rw-r--r--src/tools/clippy/tests/ui/vtable_address_comparisons.stderr83
-rw-r--r--src/tools/clippy/tests/ui/while_let_loop.rs145
-rw-r--r--src/tools/clippy/tests/ui/while_let_loop.stderr63
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.fixed453
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.rs453
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.stderr160
-rw-r--r--src/tools/clippy/tests/ui/wild_in_or_pats.rs36
-rw-r--r--src/tools/clippy/tests/ui/wild_in_or_pats.stderr35
-rw-r--r--src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed104
-rw-r--r--src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs104
-rw-r--r--src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr44
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.fixed245
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.rs246
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.stderr132
-rw-r--r--src/tools/clippy/tests/ui/write_literal.rs43
-rw-r--r--src/tools/clippy/tests/ui/write_literal.stderr135
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.rs27
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.stderr112
-rw-r--r--src/tools/clippy/tests/ui/write_with_newline.rs59
-rw-r--r--src/tools/clippy/tests/ui/write_with_newline.stderr133
-rw-r--r--src/tools/clippy/tests/ui/writeln_empty_string.fixed20
-rw-r--r--src/tools/clippy/tests/ui/writeln_empty_string.rs20
-rw-r--r--src/tools/clippy/tests/ui/writeln_empty_string.stderr16
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention.rs206
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention.stderr195
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention2.rs116
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention2.stderr19
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs29
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr19
-rw-r--r--src/tools/clippy/tests/ui/zero_div_zero.rs13
-rw-r--r--src/tools/clippy/tests/ui/zero_div_zero.stderr35
-rw-r--r--src/tools/clippy/tests/ui/zero_offset.rs19
-rw-r--r--src/tools/clippy/tests/ui/zero_offset.stderr52
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.fixed14
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.rs14
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.stderr34
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs68
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr107
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs68
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr107
1707 files changed, 120826 insertions, 0 deletions
diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs
new file mode 100644
index 000000000..f682b280c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs
@@ -0,0 +1,61 @@
+#![warn(clippy::absurd_extreme_comparisons)]
+#![allow(
+ unused,
+ clippy::eq_op,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::needless_pass_by_value
+)]
+
+#[rustfmt::skip]
+fn main() {
+ const Z: u32 = 0;
+ let u: u32 = 42;
+ u <= 0;
+ u <= Z;
+ u < Z;
+ Z >= u;
+ Z > u;
+ u > u32::MAX;
+ u >= u32::MAX;
+ u32::MAX < u;
+ u32::MAX <= u;
+ 1-1 > u;
+ u >= !0;
+ u <= 12 - 2*6;
+ let i: i8 = 0;
+ i < -127 - 1;
+ i8::MAX >= i;
+ 3-7 < i32::MIN;
+ let b = false;
+ b >= true;
+ false > b;
+ u > 0; // ok
+ // this is handled by clippy::unit_cmp
+ () < {};
+}
+
+use std::cmp::{Ordering, PartialEq, PartialOrd};
+
+#[derive(PartialEq, Eq, PartialOrd)]
+pub struct U(u64);
+
+impl PartialEq<u32> for U {
+ fn eq(&self, other: &u32) -> bool {
+ self.eq(&U(u64::from(*other)))
+ }
+}
+impl PartialOrd<u32> for U {
+ fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
+ self.partial_cmp(&U(u64::from(*other)))
+ }
+}
+
+pub fn foo(val: U) -> bool {
+ val > u32::MAX
+}
+
+pub fn bar(len: u64) -> bool {
+ // This is OK as we are casting from target sized to fixed size
+ len >= usize::MAX as u64
+}
diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr
new file mode 100644
index 000000000..6de554378
--- /dev/null
+++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr
@@ -0,0 +1,147 @@
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:14:5
+ |
+LL | u <= 0;
+ | ^^^^^^
+ |
+ = note: `-D clippy::absurd-extreme-comparisons` implied by `-D warnings`
+ = help: because `0` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 0` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:15:5
+ |
+LL | u <= Z;
+ | ^^^^^^
+ |
+ = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:16:5
+ |
+LL | u < Z;
+ | ^^^^^
+ |
+ = help: because `Z` is the minimum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:17:5
+ |
+LL | Z >= u;
+ | ^^^^^^
+ |
+ = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:18:5
+ |
+LL | Z > u;
+ | ^^^^^
+ |
+ = help: because `Z` is the minimum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:19:5
+ |
+LL | u > u32::MAX;
+ | ^^^^^^^^^^^^
+ |
+ = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:20:5
+ |
+LL | u >= u32::MAX;
+ | ^^^^^^^^^^^^^
+ |
+ = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:21:5
+ |
+LL | u32::MAX < u;
+ | ^^^^^^^^^^^^
+ |
+ = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:22:5
+ |
+LL | u32::MAX <= u;
+ | ^^^^^^^^^^^^^
+ |
+ = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:23:5
+ |
+LL | 1-1 > u;
+ | ^^^^^^^
+ |
+ = help: because `1-1` is the minimum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:24:5
+ |
+LL | u >= !0;
+ | ^^^^^^^
+ |
+ = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:25:5
+ |
+LL | u <= 12 - 2*6;
+ | ^^^^^^^^^^^^^
+ |
+ = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:27:5
+ |
+LL | i < -127 - 1;
+ | ^^^^^^^^^^^^
+ |
+ = help: because `-127 - 1` is the minimum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:28:5
+ |
+LL | i8::MAX >= i;
+ | ^^^^^^^^^^^^
+ |
+ = help: because `i8::MAX` is the maximum value for this type, this comparison is always true
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:29:5
+ |
+LL | 3-7 < i32::MIN;
+ | ^^^^^^^^^^^^^^
+ |
+ = help: because `i32::MIN` is the minimum value for this type, this comparison is always false
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:31:5
+ |
+LL | b >= true;
+ | ^^^^^^^^^
+ |
+ = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead
+
+error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
+ --> $DIR/absurd-extreme-comparisons.rs:32:5
+ |
+LL | false > b;
+ | ^^^^^^^^^
+ |
+ = help: because `false` is the minimum value for this type, this comparison is always false
+
+error: <-comparison of unit values detected. This will always be false
+ --> $DIR/absurd-extreme-comparisons.rs:35:5
+ |
+LL | () < {};
+ | ^^^^^^^
+ |
+ = note: `#[deny(clippy::unit_cmp)]` on by default
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
new file mode 100644
index 000000000..1a0d4e886
--- /dev/null
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
@@ -0,0 +1,14 @@
+#![feature(lint_reasons)]
+#![deny(clippy::allow_attributes_without_reason)]
+
+// These should trigger the lint
+#[allow(dead_code)]
+#[allow(dead_code, deprecated)]
+// These should be fine
+#[allow(dead_code, reason = "This should be allowed")]
+#[warn(dyn_drop, reason = "Warnings can also have reasons")]
+#[warn(deref_nullptr)]
+#[deny(deref_nullptr)]
+#[forbid(deref_nullptr)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
new file mode 100644
index 000000000..cd040a144
--- /dev/null
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
@@ -0,0 +1,23 @@
+error: `allow` attribute without specifying a reason
+ --> $DIR/allow_attributes_without_reason.rs:5:1
+ |
+LL | #[allow(dead_code)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/allow_attributes_without_reason.rs:2:9
+ |
+LL | #![deny(clippy::allow_attributes_without_reason)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: try adding a reason at the end with `, reason = ".."`
+
+error: `allow` attribute without specifying a reason
+ --> $DIR/allow_attributes_without_reason.rs:6:1
+ |
+LL | #[allow(dead_code, deprecated)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a reason at the end with `, reason = ".."`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
new file mode 100644
index 000000000..e69b40f35
--- /dev/null
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
@@ -0,0 +1,67 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![feature(exclusive_range_pattern)]
+#![feature(stmt_expr_attributes)]
+#![warn(clippy::almost_complete_letter_range)]
+#![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
+
+macro_rules! a {
+ () => {
+ 'a'
+ };
+}
+
+fn main() {
+ #[rustfmt::skip]
+ {
+ let _ = ('a') ..='z';
+ let _ = 'A' ..= ('Z');
+ }
+
+ let _ = 'b'..'z';
+ let _ = 'B'..'Z';
+
+ let _ = (b'a')..=(b'z');
+ let _ = b'A'..=b'Z';
+
+ let _ = b'b'..b'z';
+ let _ = b'B'..b'Z';
+
+ let _ = a!()..='z';
+
+ let _ = match 0u8 {
+ b'a'..=b'z' if true => 1,
+ b'A'..=b'Z' if true => 2,
+ b'b'..b'z' => 3,
+ b'B'..b'Z' => 4,
+ _ => 5,
+ };
+
+ let _ = match 'x' {
+ 'a'..='z' if true => 1,
+ 'A'..='Z' if true => 2,
+ 'b'..'z' => 3,
+ 'B'..'Z' => 4,
+ _ => 5,
+ };
+}
+
+fn _under_msrv() {
+ #![clippy::msrv = "1.25"]
+ let _ = match 'a' {
+ 'a'...'z' => 1,
+ _ => 2,
+ };
+}
+
+fn _meets_msrv() {
+ #![clippy::msrv = "1.26"]
+ let _ = 'a'..='z';
+ let _ = match 'a' {
+ 'a'..='z' => 1,
+ _ => 2,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.rs b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs
new file mode 100644
index 000000000..f2240981d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![feature(exclusive_range_pattern)]
+#![feature(stmt_expr_attributes)]
+#![warn(clippy::almost_complete_letter_range)]
+#![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
+
+macro_rules! a {
+ () => {
+ 'a'
+ };
+}
+
+fn main() {
+ #[rustfmt::skip]
+ {
+ let _ = ('a') ..'z';
+ let _ = 'A' .. ('Z');
+ }
+
+ let _ = 'b'..'z';
+ let _ = 'B'..'Z';
+
+ let _ = (b'a')..(b'z');
+ let _ = b'A'..b'Z';
+
+ let _ = b'b'..b'z';
+ let _ = b'B'..b'Z';
+
+ let _ = a!()..'z';
+
+ let _ = match 0u8 {
+ b'a'..b'z' if true => 1,
+ b'A'..b'Z' if true => 2,
+ b'b'..b'z' => 3,
+ b'B'..b'Z' => 4,
+ _ => 5,
+ };
+
+ let _ = match 'x' {
+ 'a'..'z' if true => 1,
+ 'A'..'Z' if true => 2,
+ 'b'..'z' => 3,
+ 'B'..'Z' => 4,
+ _ => 5,
+ };
+}
+
+fn _under_msrv() {
+ #![clippy::msrv = "1.25"]
+ let _ = match 'a' {
+ 'a'..'z' => 1,
+ _ => 2,
+ };
+}
+
+fn _meets_msrv() {
+ #![clippy::msrv = "1.26"]
+ let _ = 'a'..'z';
+ let _ = match 'a' {
+ 'a'..'z' => 1,
+ _ => 2,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
new file mode 100644
index 000000000..5b5dc40ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
@@ -0,0 +1,100 @@
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:20:17
+ |
+LL | let _ = ('a') ..'z';
+ | ^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+ |
+ = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:21:17
+ |
+LL | let _ = 'A' .. ('Z');
+ | ^^^^--^^^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:27:13
+ |
+LL | let _ = (b'a')..(b'z');
+ | ^^^^^^--^^^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:28:13
+ |
+LL | let _ = b'A'..b'Z';
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:33:13
+ |
+LL | let _ = a!()..'z';
+ | ^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:36:9
+ |
+LL | b'a'..b'z' if true => 1,
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:37:9
+ |
+LL | b'A'..b'Z' if true => 2,
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:44:9
+ |
+LL | 'a'..'z' if true => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:45:9
+ |
+LL | 'A'..'Z' if true => 2,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:55:9
+ |
+LL | 'a'..'z' => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `...`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:62:13
+ |
+LL | let _ = 'a'..'z';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii letter range
+ --> $DIR/almost_complete_letter_range.rs:64:9
+ |
+LL | 'a'..'z' => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/approx_const.rs b/src/tools/clippy/tests/ui/approx_const.rs
new file mode 100644
index 000000000..ccdbd34f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/approx_const.rs
@@ -0,0 +1,64 @@
+#[warn(clippy::approx_constant)]
+#[allow(clippy::similar_names)]
+fn main() {
+ let my_e = 2.7182;
+ let almost_e = 2.718;
+ let no_e = 2.71;
+
+ let my_1_frac_pi = 0.3183;
+ let no_1_frac_pi = 0.31;
+
+ let my_frac_1_sqrt_2 = 0.70710678;
+ let almost_frac_1_sqrt_2 = 0.70711;
+ let my_frac_1_sqrt_2 = 0.707;
+
+ let my_frac_2_pi = 0.63661977;
+ let no_frac_2_pi = 0.636;
+
+ let my_frac_2_sq_pi = 1.128379;
+ let no_frac_2_sq_pi = 1.128;
+
+ let my_frac_pi_2 = 1.57079632679;
+ let no_frac_pi_2 = 1.5705;
+
+ let my_frac_pi_3 = 1.04719755119;
+ let no_frac_pi_3 = 1.047;
+
+ let my_frac_pi_4 = 0.785398163397;
+ let no_frac_pi_4 = 0.785;
+
+ let my_frac_pi_6 = 0.523598775598;
+ let no_frac_pi_6 = 0.523;
+
+ let my_frac_pi_8 = 0.3926990816987;
+ let no_frac_pi_8 = 0.392;
+
+ let my_ln_10 = 2.302585092994046;
+ let no_ln_10 = 2.303;
+
+ let my_ln_2 = 0.6931471805599453;
+ let no_ln_2 = 0.693;
+
+ let my_log10_e = 0.4342944819032518;
+ let no_log10_e = 0.434;
+
+ let my_log2_e = 1.4426950408889634;
+ let no_log2_e = 1.442;
+
+ let log2_10 = 3.321928094887362;
+ let no_log2_10 = 3.321;
+
+ let log10_2 = 0.301029995663981;
+ let no_log10_2 = 0.301;
+
+ let my_pi = 3.1415;
+ let almost_pi = 3.14;
+ let no_pi = 3.15;
+
+ let my_sq2 = 1.4142;
+ let no_sq2 = 1.414;
+
+ let my_tau = 6.2832;
+ let almost_tau = 6.28;
+ let no_tau = 6.3;
+}
diff --git a/src/tools/clippy/tests/ui/approx_const.stderr b/src/tools/clippy/tests/ui/approx_const.stderr
new file mode 100644
index 000000000..4da1b8215
--- /dev/null
+++ b/src/tools/clippy/tests/ui/approx_const.stderr
@@ -0,0 +1,187 @@
+error: approximate value of `f{32, 64}::consts::E` found
+ --> $DIR/approx_const.rs:4:16
+ |
+LL | let my_e = 2.7182;
+ | ^^^^^^
+ |
+ = note: `-D clippy::approx-constant` implied by `-D warnings`
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::E` found
+ --> $DIR/approx_const.rs:5:20
+ |
+LL | let almost_e = 2.718;
+ | ^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found
+ --> $DIR/approx_const.rs:8:24
+ |
+LL | let my_1_frac_pi = 0.3183;
+ | ^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
+ --> $DIR/approx_const.rs:11:28
+ |
+LL | let my_frac_1_sqrt_2 = 0.70710678;
+ | ^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
+ --> $DIR/approx_const.rs:12:32
+ |
+LL | let almost_frac_1_sqrt_2 = 0.70711;
+ | ^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found
+ --> $DIR/approx_const.rs:15:24
+ |
+LL | let my_frac_2_pi = 0.63661977;
+ | ^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found
+ --> $DIR/approx_const.rs:18:27
+ |
+LL | let my_frac_2_sq_pi = 1.128379;
+ | ^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found
+ --> $DIR/approx_const.rs:21:24
+ |
+LL | let my_frac_pi_2 = 1.57079632679;
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found
+ --> $DIR/approx_const.rs:24:24
+ |
+LL | let my_frac_pi_3 = 1.04719755119;
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found
+ --> $DIR/approx_const.rs:27:24
+ |
+LL | let my_frac_pi_4 = 0.785398163397;
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found
+ --> $DIR/approx_const.rs:30:24
+ |
+LL | let my_frac_pi_6 = 0.523598775598;
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found
+ --> $DIR/approx_const.rs:33:24
+ |
+LL | let my_frac_pi_8 = 0.3926990816987;
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LN_10` found
+ --> $DIR/approx_const.rs:36:20
+ |
+LL | let my_ln_10 = 2.302585092994046;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LN_2` found
+ --> $DIR/approx_const.rs:39:19
+ |
+LL | let my_ln_2 = 0.6931471805599453;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG10_E` found
+ --> $DIR/approx_const.rs:42:22
+ |
+LL | let my_log10_e = 0.4342944819032518;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_E` found
+ --> $DIR/approx_const.rs:45:21
+ |
+LL | let my_log2_e = 1.4426950408889634;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+ --> $DIR/approx_const.rs:48:19
+ |
+LL | let log2_10 = 3.321928094887362;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG10_2` found
+ --> $DIR/approx_const.rs:51:19
+ |
+LL | let log10_2 = 0.301029995663981;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::PI` found
+ --> $DIR/approx_const.rs:54:17
+ |
+LL | let my_pi = 3.1415;
+ | ^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::PI` found
+ --> $DIR/approx_const.rs:55:21
+ |
+LL | let almost_pi = 3.14;
+ | ^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::SQRT_2` found
+ --> $DIR/approx_const.rs:58:18
+ |
+LL | let my_sq2 = 1.4142;
+ | ^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::TAU` found
+ --> $DIR/approx_const.rs:61:18
+ |
+LL | let my_tau = 6.2832;
+ | ^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::TAU` found
+ --> $DIR/approx_const.rs:62:22
+ |
+LL | let almost_tau = 6.28;
+ | ^^^^
+ |
+ = help: consider using the constant directly
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/arithmetic.fixed b/src/tools/clippy/tests/ui/arithmetic.fixed
new file mode 100644
index 000000000..a2a1c4394
--- /dev/null
+++ b/src/tools/clippy/tests/ui/arithmetic.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#![allow(clippy::unnecessary_owned_empty_strings)]
+#![feature(saturating_int_impl)]
+#![warn(clippy::arithmetic)]
+
+use core::num::{Saturating, Wrapping};
+
+pub fn hard_coded_allowed() {
+ let _ = Saturating(0u32) + Saturating(0u32);
+ let _ = String::new() + "";
+ let _ = Wrapping(0u32) + Wrapping(0u32);
+
+ let saturating: Saturating<u32> = Saturating(0u32);
+ let string: String = String::new();
+ let wrapping: Wrapping<u32> = Wrapping(0u32);
+
+ let inferred_saturating = saturating + saturating;
+ let inferred_string = string + "";
+ let inferred_wrapping = wrapping + wrapping;
+
+ let _ = inferred_saturating + inferred_saturating;
+ let _ = inferred_string + "";
+ let _ = inferred_wrapping + inferred_wrapping;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/arithmetic.rs b/src/tools/clippy/tests/ui/arithmetic.rs
new file mode 100644
index 000000000..a2a1c4394
--- /dev/null
+++ b/src/tools/clippy/tests/ui/arithmetic.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#![allow(clippy::unnecessary_owned_empty_strings)]
+#![feature(saturating_int_impl)]
+#![warn(clippy::arithmetic)]
+
+use core::num::{Saturating, Wrapping};
+
+pub fn hard_coded_allowed() {
+ let _ = Saturating(0u32) + Saturating(0u32);
+ let _ = String::new() + "";
+ let _ = Wrapping(0u32) + Wrapping(0u32);
+
+ let saturating: Saturating<u32> = Saturating(0u32);
+ let string: String = String::new();
+ let wrapping: Wrapping<u32> = Wrapping(0u32);
+
+ let inferred_saturating = saturating + saturating;
+ let inferred_string = string + "";
+ let inferred_wrapping = wrapping + wrapping;
+
+ let _ = inferred_saturating + inferred_saturating;
+ let _ = inferred_string + "";
+ let _ = inferred_wrapping + inferred_wrapping;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs
new file mode 100644
index 000000000..ba4394def
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_conversions.rs
@@ -0,0 +1,20 @@
+// aux-build:macro_rules.rs
+
+#![warn(clippy::as_conversions)]
+#![allow(clippy::borrow_as_ptr)]
+
+#[macro_use]
+extern crate macro_rules;
+
+fn with_external_macro() {
+ as_conv_with_arg!(0u32 as u64);
+ as_conv!();
+}
+
+fn main() {
+ let i = 0u32 as u64;
+
+ let j = &i as *const u64 as *mut u64;
+
+ with_external_macro();
+}
diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr
new file mode 100644
index 000000000..d11b56171
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_conversions.stderr
@@ -0,0 +1,27 @@
+error: using a potentially dangerous silent `as` conversion
+ --> $DIR/as_conversions.rs:15:13
+ |
+LL | let i = 0u32 as u64;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::as-conversions` implied by `-D warnings`
+ = help: consider using a safe wrapper for this conversion
+
+error: using a potentially dangerous silent `as` conversion
+ --> $DIR/as_conversions.rs:17:13
+ |
+LL | let j = &i as *const u64 as *mut u64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a safe wrapper for this conversion
+
+error: using a potentially dangerous silent `as` conversion
+ --> $DIR/as_conversions.rs:17:13
+ |
+LL | let j = &i as *const u64 as *mut u64;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a safe wrapper for this conversion
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/as_underscore.fixed b/src/tools/clippy/tests/ui/as_underscore.fixed
new file mode 100644
index 000000000..948f6d8e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_underscore.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+
+#![warn(clippy::as_underscore)]
+
+fn foo(_n: usize) {}
+
+fn main() {
+ let n: u16 = 256;
+ foo(n as usize);
+
+ let n = 0_u128;
+ let _n: u8 = n as u8;
+}
diff --git a/src/tools/clippy/tests/ui/as_underscore.rs b/src/tools/clippy/tests/ui/as_underscore.rs
new file mode 100644
index 000000000..97785ed08
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_underscore.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+
+#![warn(clippy::as_underscore)]
+
+fn foo(_n: usize) {}
+
+fn main() {
+ let n: u16 = 256;
+ foo(n as _);
+
+ let n = 0_u128;
+ let _n: u8 = n as _;
+}
diff --git a/src/tools/clippy/tests/ui/as_underscore.stderr b/src/tools/clippy/tests/ui/as_underscore.stderr
new file mode 100644
index 000000000..d7cd58d96
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_underscore.stderr
@@ -0,0 +1,20 @@
+error: using `as _` conversion
+ --> $DIR/as_underscore.rs:9:9
+ |
+LL | foo(n as _);
+ | ^^^^^-
+ | |
+ | help: consider giving the type explicitly: `usize`
+ |
+ = note: `-D clippy::as-underscore` implied by `-D warnings`
+
+error: using `as _` conversion
+ --> $DIR/as_underscore.rs:12:18
+ |
+LL | let _n: u8 = n as _;
+ | ^^^^^-
+ | |
+ | help: consider giving the type explicitly: `u8`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/asm_syntax.rs b/src/tools/clippy/tests/ui/asm_syntax.rs
new file mode 100644
index 000000000..0220bf333
--- /dev/null
+++ b/src/tools/clippy/tests/ui/asm_syntax.rs
@@ -0,0 +1,34 @@
+// only-x86_64
+// ignore-aarch64
+
+#[warn(clippy::inline_asm_x86_intel_syntax)]
+mod warn_intel {
+ pub(super) unsafe fn use_asm() {
+ use std::arch::asm;
+ asm!("");
+ asm!("", options());
+ asm!("", options(nostack));
+ asm!("", options(att_syntax));
+ asm!("", options(nostack, att_syntax));
+ }
+}
+
+#[warn(clippy::inline_asm_x86_att_syntax)]
+mod warn_att {
+ pub(super) unsafe fn use_asm() {
+ use std::arch::asm;
+ asm!("");
+ asm!("", options());
+ asm!("", options(nostack));
+ asm!("", options(att_syntax));
+ asm!("", options(nostack, att_syntax));
+ }
+}
+
+#[cfg(target_arch = "x86_64")]
+fn main() {
+ unsafe {
+ warn_att::use_asm();
+ warn_intel::use_asm();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/asm_syntax.stderr b/src/tools/clippy/tests/ui/asm_syntax.stderr
new file mode 100644
index 000000000..e9b150121
--- /dev/null
+++ b/src/tools/clippy/tests/ui/asm_syntax.stderr
@@ -0,0 +1,44 @@
+error: Intel x86 assembly syntax used
+ --> $DIR/asm_syntax.rs:8:9
+ |
+LL | asm!("");
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings`
+ = help: use AT&T x86 assembly syntax
+
+error: Intel x86 assembly syntax used
+ --> $DIR/asm_syntax.rs:9:9
+ |
+LL | asm!("", options());
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use AT&T x86 assembly syntax
+
+error: Intel x86 assembly syntax used
+ --> $DIR/asm_syntax.rs:10:9
+ |
+LL | asm!("", options(nostack));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use AT&T x86 assembly syntax
+
+error: AT&T x86 assembly syntax used
+ --> $DIR/asm_syntax.rs:23:9
+ |
+LL | asm!("", options(att_syntax));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings`
+ = help: use Intel x86 assembly syntax
+
+error: AT&T x86 assembly syntax used
+ --> $DIR/asm_syntax.rs:24:9
+ |
+LL | asm!("", options(nostack, att_syntax));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use Intel x86 assembly syntax
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs
new file mode 100644
index 000000000..7bea9563d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs
@@ -0,0 +1,39 @@
+#![allow(non_fmt_panics, clippy::needless_bool)]
+
+macro_rules! assert_const {
+ ($len:expr) => {
+ assert!($len > 0);
+ debug_assert!($len < 0);
+ };
+}
+fn main() {
+ assert!(true);
+ assert!(false);
+ assert!(true, "true message");
+ assert!(false, "false message");
+
+ let msg = "panic message";
+ assert!(false, "{}", msg.to_uppercase());
+
+ const B: bool = true;
+ assert!(B);
+
+ const C: bool = false;
+ assert!(C);
+ assert!(C, "C message");
+
+ debug_assert!(true);
+ // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
+ debug_assert!(false); // #3948
+ assert_const!(3);
+ assert_const!(-1);
+
+ // Don't lint if based on `cfg!(..)`:
+ assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
+
+ let flag: bool = cfg!(not(feature = "asdf"));
+ assert!(flag);
+
+ const CFG_FLAG: &bool = &cfg!(feature = "hey");
+ assert!(!CFG_FLAG);
+}
diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr
new file mode 100644
index 000000000..e1f818814
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr
@@ -0,0 +1,75 @@
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:10:5
+ |
+LL | assert!(true);
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::assertions-on-constants` implied by `-D warnings`
+ = help: remove it
+
+error: `assert!(false)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:11:5
+ |
+LL | assert!(false);
+ | ^^^^^^^^^^^^^^
+ |
+ = help: use `panic!()` or `unreachable!()`
+
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:12:5
+ |
+LL | assert!(true, "true message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove it
+
+error: `assert!(false, ..)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:13:5
+ |
+LL | assert!(false, "false message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `panic!(..)` or `unreachable!(..)`
+
+error: `assert!(false, ..)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:16:5
+ |
+LL | assert!(false, "{}", msg.to_uppercase());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `panic!(..)` or `unreachable!(..)`
+
+error: `assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:19:5
+ |
+LL | assert!(B);
+ | ^^^^^^^^^^
+ |
+ = help: remove it
+
+error: `assert!(false)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:22:5
+ |
+LL | assert!(C);
+ | ^^^^^^^^^^
+ |
+ = help: use `panic!()` or `unreachable!()`
+
+error: `assert!(false, ..)` should probably be replaced
+ --> $DIR/assertions_on_constants.rs:23:5
+ |
+LL | assert!(C, "C message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `panic!(..)` or `unreachable!(..)`
+
+error: `debug_assert!(true)` will be optimized out by the compiler
+ --> $DIR/assertions_on_constants.rs:25:5
+ |
+LL | debug_assert!(true);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove it
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
new file mode 100644
index 000000000..7bde72e4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
@@ -0,0 +1,69 @@
+// run-rustfix
+#![warn(clippy::assertions_on_result_states)]
+
+use std::result::Result;
+
+struct Foo;
+
+#[derive(Debug)]
+struct DebugFoo;
+
+#[derive(Copy, Clone, Debug)]
+struct CopyFoo;
+
+macro_rules! get_ok_macro {
+ () => {
+ Ok::<_, DebugFoo>(Foo)
+ };
+}
+
+fn main() {
+ // test ok
+ let r: Result<Foo, DebugFoo> = Ok(Foo);
+ debug_assert!(r.is_ok());
+ r.unwrap();
+
+ // test ok with non-debug error type
+ let r: Result<Foo, Foo> = Ok(Foo);
+ assert!(r.is_ok());
+
+ // test temporary ok
+ fn get_ok() -> Result<Foo, DebugFoo> {
+ Ok(Foo)
+ }
+ get_ok().unwrap();
+
+ // test macro ok
+ get_ok_macro!().unwrap();
+
+ // test ok that shouldn't be moved
+ let r: Result<CopyFoo, DebugFoo> = Ok(CopyFoo);
+ fn test_ref_unmoveable_ok(r: &Result<CopyFoo, DebugFoo>) {
+ assert!(r.is_ok());
+ }
+ test_ref_unmoveable_ok(&r);
+ assert!(r.is_ok());
+ r.unwrap();
+
+ // test ok that is copied
+ let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
+ r.unwrap();
+ r.unwrap();
+
+ // test reference to ok
+ let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
+ fn test_ref_copy_ok(r: &Result<CopyFoo, CopyFoo>) {
+ r.unwrap();
+ }
+ test_ref_copy_ok(&r);
+ r.unwrap();
+
+ // test err
+ let r: Result<DebugFoo, Foo> = Err(Foo);
+ debug_assert!(r.is_err());
+ r.unwrap_err();
+
+ // test err with non-debug value type
+ let r: Result<Foo, Foo> = Err(Foo);
+ assert!(r.is_err());
+}
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.rs b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
new file mode 100644
index 000000000..4c5af81ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
@@ -0,0 +1,69 @@
+// run-rustfix
+#![warn(clippy::assertions_on_result_states)]
+
+use std::result::Result;
+
+struct Foo;
+
+#[derive(Debug)]
+struct DebugFoo;
+
+#[derive(Copy, Clone, Debug)]
+struct CopyFoo;
+
+macro_rules! get_ok_macro {
+ () => {
+ Ok::<_, DebugFoo>(Foo)
+ };
+}
+
+fn main() {
+ // test ok
+ let r: Result<Foo, DebugFoo> = Ok(Foo);
+ debug_assert!(r.is_ok());
+ assert!(r.is_ok());
+
+ // test ok with non-debug error type
+ let r: Result<Foo, Foo> = Ok(Foo);
+ assert!(r.is_ok());
+
+ // test temporary ok
+ fn get_ok() -> Result<Foo, DebugFoo> {
+ Ok(Foo)
+ }
+ assert!(get_ok().is_ok());
+
+ // test macro ok
+ assert!(get_ok_macro!().is_ok());
+
+ // test ok that shouldn't be moved
+ let r: Result<CopyFoo, DebugFoo> = Ok(CopyFoo);
+ fn test_ref_unmoveable_ok(r: &Result<CopyFoo, DebugFoo>) {
+ assert!(r.is_ok());
+ }
+ test_ref_unmoveable_ok(&r);
+ assert!(r.is_ok());
+ r.unwrap();
+
+ // test ok that is copied
+ let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
+ assert!(r.is_ok());
+ r.unwrap();
+
+ // test reference to ok
+ let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
+ fn test_ref_copy_ok(r: &Result<CopyFoo, CopyFoo>) {
+ assert!(r.is_ok());
+ }
+ test_ref_copy_ok(&r);
+ r.unwrap();
+
+ // test err
+ let r: Result<DebugFoo, Foo> = Err(Foo);
+ debug_assert!(r.is_err());
+ assert!(r.is_err());
+
+ // test err with non-debug value type
+ let r: Result<Foo, Foo> = Err(Foo);
+ assert!(r.is_err());
+}
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
new file mode 100644
index 000000000..13c2dd877
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
@@ -0,0 +1,40 @@
+error: called `assert!` with `Result::is_ok`
+ --> $DIR/assertions_on_result_states.rs:24:5
+ |
+LL | assert!(r.is_ok());
+ | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
+ |
+ = note: `-D clippy::assertions-on-result-states` implied by `-D warnings`
+
+error: called `assert!` with `Result::is_ok`
+ --> $DIR/assertions_on_result_states.rs:34:5
+ |
+LL | assert!(get_ok().is_ok());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok().unwrap()`
+
+error: called `assert!` with `Result::is_ok`
+ --> $DIR/assertions_on_result_states.rs:37:5
+ |
+LL | assert!(get_ok_macro!().is_ok());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok_macro!().unwrap()`
+
+error: called `assert!` with `Result::is_ok`
+ --> $DIR/assertions_on_result_states.rs:50:5
+ |
+LL | assert!(r.is_ok());
+ | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
+
+error: called `assert!` with `Result::is_ok`
+ --> $DIR/assertions_on_result_states.rs:56:9
+ |
+LL | assert!(r.is_ok());
+ | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
+
+error: called `assert!` with `Result::is_err`
+ --> $DIR/assertions_on_result_states.rs:64:5
+ |
+LL | assert!(r.is_err());
+ | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed
new file mode 100644
index 000000000..da034b51c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assign_ops.fixed
@@ -0,0 +1,32 @@
+// run-rustfix
+
+use core::num::Wrapping;
+
+#[allow(dead_code, unused_assignments)]
+#[warn(clippy::assign_op_pattern)]
+fn main() {
+ let mut a = 5;
+ a += 1;
+ a += 1;
+ a -= 1;
+ a *= 99;
+ a *= 42;
+ a /= 2;
+ a %= 5;
+ a &= 1;
+ a = 1 - a;
+ a = 5 / a;
+ a = 42 % a;
+ a = 6 << a;
+ let mut s = String::new();
+ s += "bla";
+
+ // Issue #9180
+ let mut a = Wrapping(0u32);
+ a += Wrapping(1u32);
+ let mut v = vec![0u32, 1u32];
+ v[0] += v[1];
+ let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
+ v[0] = v[0] + v[1];
+ let _ = || v[0] = v[0] + v[1];
+}
diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs
new file mode 100644
index 000000000..337bb02c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assign_ops.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+
+use core::num::Wrapping;
+
+#[allow(dead_code, unused_assignments)]
+#[warn(clippy::assign_op_pattern)]
+fn main() {
+ let mut a = 5;
+ a = a + 1;
+ a = 1 + a;
+ a = a - 1;
+ a = a * 99;
+ a = 42 * a;
+ a = a / 2;
+ a = a % 5;
+ a = a & 1;
+ a = 1 - a;
+ a = 5 / a;
+ a = 42 % a;
+ a = 6 << a;
+ let mut s = String::new();
+ s = s + "bla";
+
+ // Issue #9180
+ let mut a = Wrapping(0u32);
+ a = a + Wrapping(1u32);
+ let mut v = vec![0u32, 1u32];
+ v[0] = v[0] + v[1];
+ let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
+ v[0] = v[0] + v[1];
+ let _ = || v[0] = v[0] + v[1];
+}
diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr
new file mode 100644
index 000000000..63a938ab4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assign_ops.stderr
@@ -0,0 +1,70 @@
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:9:5
+ |
+LL | a = a + 1;
+ | ^^^^^^^^^ help: replace it with: `a += 1`
+ |
+ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:10:5
+ |
+LL | a = 1 + a;
+ | ^^^^^^^^^ help: replace it with: `a += 1`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:11:5
+ |
+LL | a = a - 1;
+ | ^^^^^^^^^ help: replace it with: `a -= 1`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:12:5
+ |
+LL | a = a * 99;
+ | ^^^^^^^^^^ help: replace it with: `a *= 99`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:13:5
+ |
+LL | a = 42 * a;
+ | ^^^^^^^^^^ help: replace it with: `a *= 42`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:14:5
+ |
+LL | a = a / 2;
+ | ^^^^^^^^^ help: replace it with: `a /= 2`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:15:5
+ |
+LL | a = a % 5;
+ | ^^^^^^^^^ help: replace it with: `a %= 5`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:16:5
+ |
+LL | a = a & 1;
+ | ^^^^^^^^^ help: replace it with: `a &= 1`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:22:5
+ |
+LL | s = s + "bla";
+ | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:26:5
+ |
+LL | a = a + Wrapping(1u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops.rs:28:5
+ |
+LL | v[0] = v[0] + v[1];
+ | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs
new file mode 100644
index 000000000..f6d3a8fa3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assign_ops2.rs
@@ -0,0 +1,55 @@
+#[allow(unused_assignments)]
+#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)]
+fn main() {
+ let mut a = 5;
+ a += a + 1;
+ a += 1 + a;
+ a -= a - 1;
+ a *= a * 99;
+ a *= 42 * a;
+ a /= a / 2;
+ a %= a % 5;
+ a &= a & 1;
+ a *= a * a;
+ a = a * a * a;
+ a = a * 42 * a;
+ a = a * 2 + a;
+ a -= 1 - a;
+ a /= 5 / a;
+ a %= 42 % a;
+ a <<= 6 << a;
+}
+
+// check that we don't lint on op assign impls, because that's just the way to impl them
+
+use std::ops::{Mul, MulAssign};
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct Wrap(i64);
+
+impl Mul<i64> for Wrap {
+ type Output = Self;
+
+ fn mul(self, rhs: i64) -> Self {
+ Wrap(self.0 * rhs)
+ }
+}
+
+impl MulAssign<i64> for Wrap {
+ fn mul_assign(&mut self, rhs: i64) {
+ *self = *self * rhs
+ }
+}
+
+fn cow_add_assign() {
+ use std::borrow::Cow;
+ let mut buf = Cow::Owned(String::from("bar"));
+ let cows = Cow::Borrowed("foo");
+
+ // this can be linted
+ buf = buf + cows.clone();
+
+ // this should not as cow<str> Add is not commutative
+ buf = cows + buf;
+ println!("{}", buf);
+}
diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/assign_ops2.stderr
new file mode 100644
index 000000000..04b1dc93d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/assign_ops2.stderr
@@ -0,0 +1,146 @@
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:5:5
+ |
+LL | a += a + 1;
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings`
+help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with
+ |
+LL | a += 1;
+ | ~~~~~~
+help: or
+ |
+LL | a = a + a + 1;
+ | ~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:6:5
+ |
+LL | a += 1 + a;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with
+ |
+LL | a += 1;
+ | ~~~~~~
+help: or
+ |
+LL | a = a + 1 + a;
+ | ~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:7:5
+ |
+LL | a -= a - 1;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with
+ |
+LL | a -= 1;
+ | ~~~~~~
+help: or
+ |
+LL | a = a - (a - 1);
+ | ~~~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:8:5
+ |
+LL | a *= a * 99;
+ | ^^^^^^^^^^^
+ |
+help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with
+ |
+LL | a *= 99;
+ | ~~~~~~~
+help: or
+ |
+LL | a = a * a * 99;
+ | ~~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:9:5
+ |
+LL | a *= 42 * a;
+ | ^^^^^^^^^^^
+ |
+help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with
+ |
+LL | a *= 42;
+ | ~~~~~~~
+help: or
+ |
+LL | a = a * 42 * a;
+ | ~~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:10:5
+ |
+LL | a /= a / 2;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with
+ |
+LL | a /= 2;
+ | ~~~~~~
+help: or
+ |
+LL | a = a / (a / 2);
+ | ~~~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:11:5
+ |
+LL | a %= a % 5;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with
+ |
+LL | a %= 5;
+ | ~~~~~~
+help: or
+ |
+LL | a = a % (a % 5);
+ | ~~~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:12:5
+ |
+LL | a &= a & 1;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with
+ |
+LL | a &= 1;
+ | ~~~~~~
+help: or
+ |
+LL | a = a & a & 1;
+ | ~~~~~~~~~~~~~
+
+error: variable appears on both sides of an assignment operation
+ --> $DIR/assign_ops2.rs:13:5
+ |
+LL | a *= a * a;
+ | ^^^^^^^^^^
+ |
+help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with
+ |
+LL | a *= a;
+ | ~~~~~~
+help: or
+ |
+LL | a = a * a * a;
+ | ~~~~~~~~~~~~~
+
+error: manual implementation of an assign operation
+ --> $DIR/assign_ops2.rs:50:5
+ |
+LL | buf = buf + cows.clone();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
+ |
+ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
new file mode 100644
index 000000000..3cf380d2b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -0,0 +1,78 @@
+// run-rustfix
+#![feature(lint_reasons)]
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }.await
+ };
+ let _i = async {
+ CustomFutureType.await
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }.await
+ };
+ let _k = async || {
+ CustomFutureType.await
+ };
+ let _l = async || CustomFutureType.await;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType.await
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn check_expect_suppression() {
+ #[expect(clippy::async_yields_async)]
+ let _j = async || {
+ async {
+ 3
+ }
+ };
+}
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
new file mode 100644
index 000000000..dd4131b60
--- /dev/null
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -0,0 +1,78 @@
+// run-rustfix
+#![feature(lint_reasons)]
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }
+ };
+ let _i = async {
+ CustomFutureType
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }
+ };
+ let _k = async || {
+ CustomFutureType
+ };
+ let _l = async || CustomFutureType;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn check_expect_suppression() {
+ #[expect(clippy::async_yields_async)]
+ let _j = async || {
+ async {
+ 3
+ }
+ };
+}
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
new file mode 100644
index 000000000..b0c4215e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -0,0 +1,96 @@
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:39:9
+ |
+LL | let _h = async {
+ | ____________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+ = note: `-D clippy::async-yields-async` implied by `-D warnings`
+help: consider awaiting this value
+ |
+LL ~ async {
+LL + 3
+LL + }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:44:9
+ |
+LL | let _i = async {
+ | ____________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:50:9
+ |
+LL | let _j = async || {
+ | _______________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+help: consider awaiting this value
+ |
+LL ~ async {
+LL + 3
+LL + }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:55:9
+ |
+LL | let _k = async || {
+ | _______________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:57:23
+ |
+LL | let _l = async || CustomFutureType;
+ | ^^^^^^^^^^^^^^^^
+ | |
+ | outer async construct
+ | awaitable value not awaited
+ | help: consider awaiting this value: `CustomFutureType.await`
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:63:9
+ |
+LL | let _m = async || {
+ | _______________________-
+LL | | println!("I'm bored");
+LL | | // Some more stuff
+LL | |
+LL | | // Finally something to await
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/attrs.rs b/src/tools/clippy/tests/ui/attrs.rs
new file mode 100644
index 000000000..8df6e1942
--- /dev/null
+++ b/src/tools/clippy/tests/ui/attrs.rs
@@ -0,0 +1,45 @@
+#![warn(clippy::inline_always, clippy::deprecated_semver)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)]
+
+#[inline(always)]
+fn test_attr_lint() {
+ assert!(true)
+}
+
+#[inline(always)]
+fn false_positive_expr() {
+ unreachable!()
+}
+
+#[inline(always)]
+fn false_positive_stmt() {
+ unreachable!();
+}
+
+#[inline(always)]
+fn empty_and_false_positive_stmt() {
+ unreachable!();
+}
+
+#[deprecated(since = "forever")]
+pub const SOME_CONST: u8 = 42;
+
+#[deprecated(since = "1")]
+pub const ANOTHER_CONST: u8 = 23;
+
+#[deprecated(since = "0.1.1")]
+pub const YET_ANOTHER_CONST: u8 = 0;
+
+fn main() {
+ test_attr_lint();
+ if false {
+ false_positive_expr()
+ }
+ if false {
+ false_positive_stmt()
+ }
+ if false {
+ empty_and_false_positive_stmt()
+ }
+}
diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr
new file mode 100644
index 000000000..df4e9e20b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/attrs.stderr
@@ -0,0 +1,24 @@
+error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
+ --> $DIR/attrs.rs:5:1
+ |
+LL | #[inline(always)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::inline-always` implied by `-D warnings`
+
+error: the since field must contain a semver-compliant version
+ --> $DIR/attrs.rs:25:14
+ |
+LL | #[deprecated(since = "forever")]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::deprecated-semver` implied by `-D warnings`
+
+error: the since field must contain a semver-compliant version
+ --> $DIR/attrs.rs:28:14
+ |
+LL | #[deprecated(since = "1")]
+ | ^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/author.rs b/src/tools/clippy/tests/ui/author.rs
new file mode 100644
index 000000000..0a1be3568
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author.rs
@@ -0,0 +1,4 @@
+fn main() {
+ #[clippy::author]
+ let x: char = 0x45 as char;
+}
diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout
new file mode 100644
index 000000000..312586303
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author.stdout
@@ -0,0 +1,14 @@
+if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Cast(expr, cast_ty) = init.kind;
+ if let TyKind::Path(ref qpath) = cast_ty.kind;
+ if match_qpath(qpath, &["char"]);
+ if let ExprKind::Lit(ref lit) = expr.kind;
+ if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
+ if name.as_str() == "x";
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs
new file mode 100644
index 000000000..a7335c01b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/blocks.rs
@@ -0,0 +1,24 @@
+// edition:2018
+
+#![allow(redundant_semicolons, clippy::no_effect)]
+#![feature(stmt_expr_attributes)]
+#![feature(async_closure)]
+
+#[rustfmt::skip]
+fn main() {
+ #[clippy::author]
+ {
+ let x = 42i32;
+ let _t = 1f32;
+
+ -x;
+ };
+ #[clippy::author]
+ {
+ let expr = String::new();
+ drop(expr)
+ };
+
+ #[clippy::author]
+ async move || {};
+}
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
new file mode 100644
index 000000000..2fc4a7d1f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -0,0 +1,64 @@
+if_chain! {
+ if let ExprKind::Block(block, None) = expr.kind;
+ if block.stmts.len() == 3;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Lit(ref lit) = init.kind;
+ if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
+ if name.as_str() == "x";
+ if let StmtKind::Local(local1) = block.stmts[1].kind;
+ if let Some(init1) = local1.init;
+ if let ExprKind::Lit(ref lit1) = init1.kind;
+ if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
+ if name1.as_str() == "_t";
+ if let StmtKind::Semi(e) = block.stmts[2].kind;
+ if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
+ if let ExprKind::Path(ref qpath) = inner.kind;
+ if match_qpath(qpath, &["x"]);
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::Block(block, None) = expr.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["String", "new"]);
+ if args.is_empty();
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
+ if name.as_str() == "expr";
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Call(func1, args1) = trailing_expr.kind;
+ if let ExprKind::Path(ref qpath1) = func1.kind;
+ if match_qpath(qpath1, &["drop"]);
+ if args1.len() == 1;
+ if let ExprKind::Path(ref qpath2) = args1[0].kind;
+ if match_qpath(qpath2, &["expr"]);
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
+ if let FnRetTy::DefaultReturn(_) = fn_decl.output;
+ let expr1 = &cx.tcx.hir().body(body_id).value;
+ if let ExprKind::Call(func, args) = expr1.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
+ if args.len() == 1;
+ if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
+ if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
+ let expr2 = &cx.tcx.hir().body(body_id1).value;
+ if let ExprKind::Block(block, None) = expr2.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/call.rs b/src/tools/clippy/tests/ui/author/call.rs
new file mode 100644
index 000000000..e99c3c41d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/call.rs
@@ -0,0 +1,4 @@
+fn main() {
+ #[clippy::author]
+ let _ = ::std::cmp::min(3, 4);
+}
diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout
new file mode 100644
index 000000000..266312d63
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/call.stdout
@@ -0,0 +1,16 @@
+if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
+ if args.len() == 2;
+ if let ExprKind::Lit(ref lit) = args[0].kind;
+ if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
+ if let ExprKind::Lit(ref lit1) = args[1].kind;
+ if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node;
+ if let PatKind::Wild = local.pat.kind;
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs
new file mode 100644
index 000000000..946088ab3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/if.rs
@@ -0,0 +1,17 @@
+#[allow(clippy::all)]
+
+fn main() {
+ #[clippy::author]
+ let _ = if true {
+ 1 == 1;
+ } else {
+ 2 == 2;
+ };
+
+ let a = true;
+
+ #[clippy::author]
+ if let true = a {
+ } else {
+ };
+}
diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout
new file mode 100644
index 000000000..8d92849b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/if.stdout
@@ -0,0 +1,50 @@
+if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
+ if let ExprKind::DropTemps(expr) = cond.kind;
+ if let ExprKind::Lit(ref lit) = expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Block(block, None) = then.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Binary(op, left, right) = e.kind;
+ if BinOpKind::Eq == op.node;
+ if let ExprKind::Lit(ref lit1) = left.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Lit(ref lit2) = right.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
+ if block.expr.is_none();
+ if let ExprKind::Block(block1, None) = else_expr.kind;
+ if block1.stmts.len() == 1;
+ if let StmtKind::Semi(e1) = block1.stmts[0].kind;
+ if let ExprKind::Binary(op1, left1, right1) = e1.kind;
+ if BinOpKind::Eq == op1.node;
+ if let ExprKind::Lit(ref lit3) = left1.kind;
+ if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
+ if let ExprKind::Lit(ref lit4) = right1.kind;
+ if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node;
+ if block1.expr.is_none();
+ if let PatKind::Wild = local.pat.kind;
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
+ if let ExprKind::Let(let_expr) = cond.kind;
+ if let PatKind::Lit(lit_expr) = let_expr.pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Path(ref qpath) = let_expr.init.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = then.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ if let ExprKind::Block(block1, None) = else_expr.kind;
+ if block1.stmts.is_empty();
+ if block1.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs
new file mode 100644
index 000000000..bae4570e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/issue_3849.rs
@@ -0,0 +1,14 @@
+#![allow(dead_code)]
+#![allow(clippy::zero_ptr)]
+#![allow(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::transmuting_null)]
+
+pub const ZPTR: *const usize = 0 as *const _;
+
+fn main() {
+ unsafe {
+ #[clippy::author]
+ let _: &i32 = std::mem::transmute(ZPTR);
+ let _: &i32 = std::mem::transmute(0 as *const i32);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout
new file mode 100644
index 000000000..bce4bc702
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout
@@ -0,0 +1,14 @@
+if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["std", "mem", "transmute"]);
+ if args.len() == 1;
+ if let ExprKind::Path(ref qpath1) = args[0].kind;
+ if match_qpath(qpath1, &["ZPTR"]);
+ if let PatKind::Wild = local.pat.kind;
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/loop.rs b/src/tools/clippy/tests/ui/author/loop.rs
new file mode 100644
index 000000000..d6de21631
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/loop.rs
@@ -0,0 +1,36 @@
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::never_loop, clippy::while_immutable_condition)]
+
+fn main() {
+ #[clippy::author]
+ for y in 0..10 {
+ let z = y;
+ }
+
+ #[clippy::author]
+ for _ in 0..10 {
+ break;
+ }
+
+ #[clippy::author]
+ 'label: for _ in 0..10 {
+ break 'label;
+ }
+
+ let a = true;
+
+ #[clippy::author]
+ while a {
+ break;
+ }
+
+ #[clippy::author]
+ while let true = a {
+ break;
+ }
+
+ #[clippy::author]
+ loop {
+ break;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout
new file mode 100644
index 000000000..3d9560f69
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/loop.stdout
@@ -0,0 +1,113 @@
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = pat.kind;
+ if name.as_str() == "y";
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Path(ref qpath1) = init.kind;
+ if match_qpath(qpath1, &["y"]);
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
+ if name1.as_str() == "z";
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Wild = pat.kind;
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Wild = pat.kind;
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if let Some(label) = destination.label;
+ if label.ident.as_str() == "'label";
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
+ if let ExprKind::Path(ref qpath) = condition.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
+ if let PatKind::Lit(lit_expr) = let_pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Path(ref qpath) = let_expr.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = if_then.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
+ if body.stmts.len() == 1;
+ if let StmtKind::Semi(e) = body.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if body.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/matches.rs b/src/tools/clippy/tests/ui/author/matches.rs
new file mode 100644
index 000000000..674e07ec2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/matches.rs
@@ -0,0 +1,13 @@
+#![allow(clippy::let_and_return)]
+
+fn main() {
+ #[clippy::author]
+ let a = match 42 {
+ 16 => 5,
+ 17 => {
+ let x = 3;
+ x
+ },
+ _ => 1,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout
new file mode 100644
index 000000000..38444a009
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/matches.stdout
@@ -0,0 +1,38 @@
+if_chain! {
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
+ if let ExprKind::Lit(ref lit) = scrutinee.kind;
+ if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
+ if arms.len() == 3;
+ if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
+ if let ExprKind::Lit(ref lit1) = lit_expr.kind;
+ if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
+ if arms[0].guard.is_none();
+ if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
+ if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
+ if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
+ if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
+ if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
+ if arms[1].guard.is_none();
+ if let ExprKind::Block(block, None) = arms[1].body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Local(local1) = block.stmts[0].kind;
+ if let Some(init1) = local1.init;
+ if let ExprKind::Lit(ref lit4) = init1.kind;
+ if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
+ if name.as_str() == "x";
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Path(ref qpath) = trailing_expr.kind;
+ if match_qpath(qpath, &["x"]);
+ if let PatKind::Wild = arms[2].pat.kind;
+ if arms[2].guard.is_none();
+ if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
+ if name1.as_str() == "a";
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/repeat.rs b/src/tools/clippy/tests/ui/author/repeat.rs
new file mode 100644
index 000000000..d8e9d589e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/repeat.rs
@@ -0,0 +1,5 @@
+#[allow(clippy::no_effect)]
+fn main() {
+ #[clippy::author]
+ [1_u8; 5];
+}
diff --git a/src/tools/clippy/tests/ui/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout
new file mode 100644
index 000000000..471bbce4f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/repeat.stdout
@@ -0,0 +1,12 @@
+if_chain! {
+ if let ExprKind::Repeat(value, length) = expr.kind;
+ if let ExprKind::Lit(ref lit) = value.kind;
+ if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
+ if let ArrayLen::Body(anon_const) = length;
+ let expr1 = &cx.tcx.hir().body(anon_const.body).value;
+ if let ExprKind::Lit(ref lit1) = expr1.kind;
+ if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.rs b/src/tools/clippy/tests/ui/author/struct.rs
new file mode 100644
index 000000000..5fdf3433a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/struct.rs
@@ -0,0 +1,40 @@
+#[allow(clippy::unnecessary_operation, clippy::single_match)]
+fn main() {
+ struct Test {
+ field: u32,
+ }
+
+ #[clippy::author]
+ Test {
+ field: if true { 1 } else { 0 },
+ };
+
+ let test = Test { field: 1 };
+
+ match test {
+ #[clippy::author]
+ Test { field: 1 } => {},
+ _ => {},
+ }
+
+ struct TestTuple(u32);
+
+ let test_tuple = TestTuple(1);
+
+ match test_tuple {
+ #[clippy::author]
+ TestTuple(1) => {},
+ _ => {},
+ }
+
+ struct TestMethodCall(u32);
+
+ impl TestMethodCall {
+ fn test(&self) {}
+ }
+
+ let test_method_call = TestMethodCall(1);
+
+ #[clippy::author]
+ test_method_call.test();
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout
new file mode 100644
index 000000000..5e78b7c9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/struct.stdout
@@ -0,0 +1,64 @@
+if_chain! {
+ if let ExprKind::Struct(qpath, fields, None) = expr.kind;
+ if match_qpath(qpath, &["Test"]);
+ if fields.len() == 1;
+ if fields[0].ident.as_str() == "field";
+ if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
+ if let ExprKind::DropTemps(expr1) = cond.kind;
+ if let ExprKind::Lit(ref lit) = expr1.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Block(block, None) = then.kind;
+ if block.stmts.is_empty();
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block1, None) = else_expr.kind;
+ if block1.stmts.is_empty();
+ if let Some(trailing_expr1) = block1.expr;
+ if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
+ if match_qpath(qpath, &["Test"]);
+ if fields.len() == 1;
+ if fields[0].ident.as_str() == "field";
+ if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+ if arm.guard.is_none();
+ if let ExprKind::Block(block, None) = arm.body.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
+ if match_qpath(qpath, &["TestTuple"]);
+ if fields.len() == 1;
+ if let PatKind::Lit(lit_expr) = fields[0].kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+ if arm.guard.is_none();
+ if let ExprKind::Block(block, None) = arm.body.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::MethodCall(method_name, args, _) = expr.kind;
+ if method_name.ident.as_str() == "test";
+ if args.len() == 1;
+ if let ExprKind::Path(ref qpath) = args[0].kind;
+ if match_qpath(qpath, &["test_method_call"]);
+ then {
+ // report your lint here
+ }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
new file mode 100644
index 000000000..869672d1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
@@ -0,0 +1,8 @@
+#[macro_export]
+macro_rules! undocd_unsafe {
+ () => {
+ pub unsafe fn oy_vey() {
+ unimplemented!();
+ }
+ };
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs
new file mode 100644
index 000000000..1eb77c531
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs
@@ -0,0 +1,6 @@
+#[macro_export]
+macro_rules! implicit_hasher_fn {
+ () => {
+ pub fn f(input: &HashMap<u32, u32>) {}
+ };
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
new file mode 100644
index 000000000..83a0af6b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
@@ -0,0 +1,142 @@
+#![allow(dead_code)]
+
+//! Used to test that certain lints don't trigger in imported external macros
+
+#[macro_export]
+macro_rules! foofoo {
+ () => {
+ loop {}
+ };
+}
+
+#[macro_export]
+macro_rules! must_use_unit {
+ () => {
+ #[must_use]
+ fn foo() {}
+ };
+}
+
+#[macro_export]
+macro_rules! try_err {
+ () => {
+ pub fn try_err_fn() -> Result<i32, i32> {
+ let err: i32 = 1;
+ // To avoid warnings during rustfix
+ if true { Err(err)? } else { Ok(2) }
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! string_add {
+ () => {
+ let y = "".to_owned();
+ let z = y + "...";
+ };
+}
+
+#[macro_export]
+macro_rules! take_external {
+ ($s:expr) => {
+ std::mem::replace($s, Default::default())
+ };
+}
+
+#[macro_export]
+macro_rules! option_env_unwrap_external {
+ ($env: expr) => {
+ option_env!($env).unwrap()
+ };
+ ($env: expr, $message: expr) => {
+ option_env!($env).expect($message)
+ };
+}
+
+#[macro_export]
+macro_rules! ref_arg_binding {
+ () => {
+ let ref _y = 42;
+ };
+}
+
+#[macro_export]
+macro_rules! ref_arg_function {
+ () => {
+ fn fun_example(ref _x: usize) {}
+ };
+}
+
+#[macro_export]
+macro_rules! as_conv_with_arg {
+ (0u32 as u64) => {
+ ()
+ };
+}
+
+#[macro_export]
+macro_rules! as_conv {
+ () => {
+ 0u32 as u64
+ };
+}
+
+#[macro_export]
+macro_rules! large_enum_variant {
+ () => {
+ enum LargeEnumInMacro {
+ A(i32),
+ B([i32; 8000]),
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! field_reassign_with_default {
+ () => {
+ #[derive(Default)]
+ struct A {
+ pub i: i32,
+ pub j: i64,
+ }
+ fn lint() {
+ let mut a: A = Default::default();
+ a.i = 42;
+ a;
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! default_numeric_fallback {
+ () => {
+ let x = 22;
+ };
+}
+
+#[macro_export]
+macro_rules! mut_mut {
+ () => {
+ let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;
+ };
+}
+
+#[macro_export]
+macro_rules! ptr_as_ptr_cast {
+ ($ptr: ident) => {
+ $ptr as *const i32
+ };
+}
+
+#[macro_export]
+macro_rules! manual_rem_euclid {
+ () => {
+ let value: i32 = 5;
+ let _: i32 = ((value % 4) + 4) % 4;
+ };
+}
+
+#[macro_export]
+macro_rules! equatable_if_let {
+ ($a:ident) => {{ if let 2 = $a {} }};
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
new file mode 100644
index 000000000..ecb55d8cb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
@@ -0,0 +1,60 @@
+extern crate macro_rules;
+
+// STMT
+#[macro_export]
+macro_rules! pub_macro {
+ () => {
+ let _ = "hello Mr. Vonnegut";
+ };
+}
+
+pub mod inner {
+ pub use super::*;
+
+ // RE-EXPORT
+ // this will stick in `inner` module
+ pub use macro_rules::foofoo;
+ pub use macro_rules::try_err;
+
+ pub mod nested {
+ pub use macro_rules::string_add;
+ }
+
+ // ITEM
+ #[macro_export]
+ macro_rules! inner_mod_macro {
+ () => {
+ #[allow(dead_code)]
+ pub struct Tardis;
+ };
+ }
+}
+
+// EXPR
+#[macro_export]
+macro_rules! function_macro {
+ () => {
+ if true {
+ } else {
+ }
+ };
+}
+
+// TYPE
+#[macro_export]
+macro_rules! ty_macro {
+ () => {
+ Vec<u8>
+ };
+}
+
+mod extern_exports {
+ pub(super) mod private_inner {
+ #[macro_export]
+ macro_rules! pub_in_private_macro {
+ ($name:ident) => {
+ let $name = String::from("secrets and lies");
+ };
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs
new file mode 100644
index 000000000..420232f9f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs
@@ -0,0 +1,8 @@
+// Stripped down version of the ErrorKind enum of std
+#[non_exhaustive]
+pub enum ErrorKind {
+ NotFound,
+ PermissionDenied,
+ #[doc(hidden)]
+ Uncategorized,
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
new file mode 100644
index 000000000..f9bc9436b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
@@ -0,0 +1,64 @@
+#![allow(dead_code, unused_variables, clippy::return_self_not_must_use)]
+
+/// Utility macro to test linting behavior in `option_methods()`
+/// The lints included in `option_methods()` should not lint if the call to map is partially
+/// within a macro
+#[macro_export]
+macro_rules! opt_map {
+ ($opt:expr, $map:expr) => {
+ ($opt).map($map)
+ };
+}
+
+/// Struct to generate false positive for Iterator-based lints
+#[derive(Copy, Clone)]
+pub struct IteratorFalsePositives {
+ pub foo: u32,
+}
+
+impl IteratorFalsePositives {
+ pub fn filter(self) -> IteratorFalsePositives {
+ self
+ }
+
+ pub fn next(self) -> IteratorFalsePositives {
+ self
+ }
+
+ pub fn find(self) -> Option<u32> {
+ Some(self.foo)
+ }
+
+ pub fn position(self) -> Option<u32> {
+ Some(self.foo)
+ }
+
+ pub fn rposition(self) -> Option<u32> {
+ Some(self.foo)
+ }
+
+ pub fn nth(self, n: usize) -> Option<u32> {
+ Some(self.foo)
+ }
+
+ pub fn skip(self, _: usize) -> IteratorFalsePositives {
+ self
+ }
+
+ pub fn skip_while(self) -> IteratorFalsePositives {
+ self
+ }
+
+ pub fn count(self) -> usize {
+ self.foo as usize
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct IteratorMethodFalsePositives;
+
+impl IteratorMethodFalsePositives {
+ pub fn filter(&self, _s: i32) -> std::vec::IntoIter<i32> {
+ unimplemented!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
new file mode 100644
index 000000000..ae2cc2492
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
@@ -0,0 +1,101 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
+#![allow(incomplete_features)]
+#![allow(clippy::useless_conversion)]
+
+extern crate proc_macro;
+extern crate quote;
+extern crate syn;
+
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::parse_macro_input;
+use syn::spanned::Spanned;
+use syn::token::Star;
+use syn::{
+ parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
+};
+
+#[proc_macro_attribute]
+pub fn dummy(_args: TokenStream, input: TokenStream) -> TokenStream {
+ input
+}
+
+#[proc_macro_attribute]
+pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
+ let mut item = parse_macro_input!(input as ItemTrait);
+ for inner in &mut item.items {
+ if let TraitItem::Method(method) = inner {
+ let sig = &method.sig;
+ let block = &mut method.default;
+ if let Some(block) = block {
+ let brace = block.brace_token;
+
+ let my_block = quote_spanned!( brace.span => {
+ // Should not trigger `empty_line_after_outer_attr`
+ #[crate_type = "lib"]
+ #sig #block
+ Vec::new()
+ });
+ *block = parse_quote!(#my_block);
+ }
+ }
+ }
+ TokenStream::from(quote!(#item))
+}
+
+#[proc_macro_attribute]
+pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
+ fn make_name(count: usize) -> String {
+ format!("'life{}", count)
+ }
+
+ fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
+ let arg = sig.inputs.first_mut()?;
+ if let FnArg::Typed(PatType { pat, .. }) = arg {
+ if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
+ if ident == "self" {
+ return Some(arg);
+ }
+ }
+ }
+ None
+ }
+
+ let mut elided = 0;
+ let mut item = parse_macro_input!(input as ItemImpl);
+
+ // Look for methods having arbitrary self type taken by &mut ref
+ for inner in &mut item.items {
+ if let ImplItem::Method(method) = inner {
+ if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
+ if let box Type::Reference(reference) = &mut pat_type.ty {
+ // Target only unnamed lifetimes
+ let name = match &reference.lifetime {
+ Some(lt) if lt.ident == "_" => make_name(elided),
+ None => make_name(elided),
+ _ => continue,
+ };
+ elided += 1;
+
+ // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
+ // In order to avoid adding the dependency, get a default span from a non-existent token.
+ // A default span is needed to mark the code as coming from expansion.
+ let span = Star::default().span();
+
+ // Replace old lifetime with the named one
+ let lifetime = Lifetime::new(&name, span);
+ reference.lifetime = Some(parse_quote!(#lifetime));
+
+ // Add lifetime to the generics of the method
+ method.sig.generics.params.push(parse_quote!(#lifetime));
+ }
+ }
+ }
+ }
+
+ TokenStream::from(quote!(#item))
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
new file mode 100644
index 000000000..a89a06308
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -0,0 +1,88 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(repr128, proc_macro_quote)]
+#![allow(incomplete_features)]
+#![allow(clippy::field_reassign_with_default)]
+#![allow(clippy::eq_op)]
+
+extern crate proc_macro;
+
+use proc_macro::{quote, TokenStream};
+
+#[proc_macro_derive(DeriveSomething)]
+pub fn derive(_: TokenStream) -> TokenStream {
+ // Should not trigger `used_underscore_binding`
+ let _inside_derive = 1;
+ assert_eq!(_inside_derive, _inside_derive);
+
+ let output = quote! {
+ // Should not trigger `useless_attribute`
+ #[allow(dead_code)]
+ extern crate rustc_middle;
+ };
+ output
+}
+
+#[proc_macro_derive(FieldReassignWithDefault)]
+pub fn derive_foo(_input: TokenStream) -> TokenStream {
+ quote! {
+ #[derive(Default)]
+ struct A {
+ pub i: i32,
+ pub j: i64,
+ }
+ #[automatically_derived]
+ fn lint() {
+ let mut a: A = Default::default();
+ a.i = 42;
+ a;
+ }
+ }
+}
+
+#[proc_macro_derive(StructAUseSelf)]
+pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream {
+ quote! {
+ struct A;
+ impl A {
+ fn new() -> A {
+ A
+ }
+ }
+ }
+}
+
+#[proc_macro_derive(ClippyMiniMacroTest)]
+pub fn mini_macro(_: TokenStream) -> TokenStream {
+ quote!(
+ #[allow(unused)]
+ fn needless_take_by_value(s: String) {
+ println!("{}", s.len());
+ }
+ #[allow(unused)]
+ fn needless_loop(items: &[u8]) {
+ for i in 0..items.len() {
+ println!("{}", items[i]);
+ }
+ }
+ fn line_wrapper() {
+ println!("{}", line!());
+ }
+ )
+}
+
+#[proc_macro_derive(ExtraLifetimeDerive)]
+#[allow(unused)]
+pub fn extra_lifetime(_input: TokenStream) -> TokenStream {
+ quote!(
+ pub struct ExtraLifetime;
+
+ impl<'b> ExtraLifetime {
+ pub fn something<'c>() -> Self {
+ Self
+ }
+ }
+ )
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
new file mode 100644
index 000000000..a2ef0fe82
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
@@ -0,0 +1,74 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
+
+fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
+ match iter.next() {
+ Some(TokenTree::Ident(i)) => i,
+ _ => panic!("expected ident"),
+ }
+}
+
+#[proc_macro_derive(DeriveBadSpan)]
+pub fn derive_bad_span(input: TokenStream) -> TokenStream {
+ let mut input = input.into_iter();
+ assert_eq!(read_ident(&mut input).to_string(), "struct");
+ let ident = read_ident(&mut input);
+ let mut tys = match input.next() {
+ Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
+ _ => panic!(),
+ };
+ let field1 = read_ident(&mut tys);
+ tys.next();
+ let field2 = read_ident(&mut tys);
+
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("impl", Span::call_site()).into(),
+ ident.into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("fn", Span::call_site()).into(),
+ Ident::new("_foo", Span::call_site()).into(),
+ Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("if", field1.span()).into(),
+ Ident::new("true", field1.span()).into(),
+ {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(field1.span());
+ group.into()
+ },
+ Ident::new("if", field2.span()).into(),
+ Ident::new("true", field2.span()).into(),
+ {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(field2.span());
+ group.into()
+ },
+ ]
+ .iter()
+ .cloned(),
+ ),
+ )
+ .into(),
+ ]
+ .iter()
+ .cloned(),
+ ),
+ )
+ .into(),
+ ]
+ .iter()
+ .cloned(),
+ )
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs
new file mode 100644
index 000000000..3c40f7746
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs
@@ -0,0 +1,18 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn unsafe_block(input: TokenStream) -> TokenStream {
+ let span = input.into_iter().next().unwrap().span();
+ TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(span);
+ TokenTree::Group(group)
+ }])
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs
new file mode 100644
index 000000000..8ea631f2b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs
@@ -0,0 +1,32 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn with_span(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let span = iter.next().unwrap().span();
+ let mut res = TokenStream::new();
+ write_with_span(span, iter, &mut res);
+ res
+}
+
+fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
+ for mut tt in input {
+ if let TokenTree::Group(g) = tt {
+ let mut stream = TokenStream::new();
+ write_with_span(s, g.stream().into_iter(), &mut stream);
+ let mut group = Group::new(g.delimiter(), stream);
+ group.set_span(s);
+ out.extend([TokenTree::Group(group)]);
+ } else {
+ tt.set_span(s);
+ out.extend([tt]);
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/test_macro.rs b/src/tools/clippy/tests/ui/auxiliary/test_macro.rs
new file mode 100644
index 000000000..624ca892a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/test_macro.rs
@@ -0,0 +1,11 @@
+pub trait A {}
+
+macro_rules! __implicit_hasher_test_macro {
+ (impl< $($impl_arg:tt),* > for $kind:ty where $($bounds:tt)*) => {
+ __implicit_hasher_test_macro!( ($($impl_arg),*) ($kind) ($($bounds)*) );
+ };
+
+ (($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => {
+ impl< $($impl_arg)* > test_macro::A for $($kind_arg)* where $($bounds)* { }
+ };
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs
new file mode 100644
index 000000000..a8a85b4ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs
@@ -0,0 +1,15 @@
+macro_rules! use_self {
+ (
+ impl $ty:ident {
+ fn func(&$this:ident) {
+ [fields($($field:ident)*)]
+ }
+ }
+ ) => (
+ impl $ty {
+ fn func(&$this) {
+ let $ty { $($field),* } = $this;
+ }
+ }
+ )
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
new file mode 100644
index 000000000..d75cdd625
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
@@ -0,0 +1,27 @@
+pub use crate::extern_exports::*;
+
+pub fn extern_foo() {}
+pub fn extern_bar() {}
+
+pub struct ExternA;
+
+pub mod inner {
+ pub mod inner_for_self_import {
+ pub fn inner_extern_foo() {}
+ pub fn inner_extern_bar() {}
+ }
+}
+
+mod extern_exports {
+ pub fn extern_exported() {}
+ pub struct ExternExportedStruct;
+ pub enum ExternExportedEnum {
+ A,
+ }
+}
+
+pub mod prelude {
+ pub mod v1 {
+ pub struct PreludeModAnywhere;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs
new file mode 100644
index 000000000..57e5b5504
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_lock.rs
@@ -0,0 +1,192 @@
+#![warn(clippy::await_holding_lock)]
+
+// When adding or modifying a test, please do the same for parking_lot::Mutex.
+mod std_mutex {
+ use super::baz;
+ use std::sync::{Mutex, RwLock};
+
+ pub async fn bad(x: &Mutex<u32>) -> u32 {
+ let guard = x.lock().unwrap();
+ baz().await
+ }
+
+ pub async fn good(x: &Mutex<u32>) -> u32 {
+ {
+ let guard = x.lock().unwrap();
+ let y = *guard + 1;
+ }
+ baz().await;
+ let guard = x.lock().unwrap();
+ 47
+ }
+
+ pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
+ let guard = x.read().unwrap();
+ baz().await
+ }
+
+ pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
+ let mut guard = x.write().unwrap();
+ baz().await
+ }
+
+ pub async fn good_rw(x: &RwLock<u32>) -> u32 {
+ {
+ let guard = x.read().unwrap();
+ let y = *guard + 1;
+ }
+ {
+ let mut guard = x.write().unwrap();
+ *guard += 1;
+ }
+ baz().await;
+ let guard = x.read().unwrap();
+ 47
+ }
+
+ pub async fn also_bad(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let guard = x.lock().unwrap();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+ }
+
+ pub async fn not_good(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let guard = x.lock().unwrap();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+ }
+
+ #[allow(clippy::manual_async_fn)]
+ pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let guard = x.lock().unwrap();
+ baz().await
+ }
+ }
+}
+
+// When adding or modifying a test, please do the same for std::Mutex.
+mod parking_lot_mutex {
+ use super::baz;
+ use parking_lot::{Mutex, RwLock};
+
+ pub async fn bad(x: &Mutex<u32>) -> u32 {
+ let guard = x.lock();
+ baz().await
+ }
+
+ pub async fn good(x: &Mutex<u32>) -> u32 {
+ {
+ let guard = x.lock();
+ let y = *guard + 1;
+ }
+ baz().await;
+ let guard = x.lock();
+ 47
+ }
+
+ pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
+ let guard = x.read();
+ baz().await
+ }
+
+ pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
+ let mut guard = x.write();
+ baz().await
+ }
+
+ pub async fn good_rw(x: &RwLock<u32>) -> u32 {
+ {
+ let guard = x.read();
+ let y = *guard + 1;
+ }
+ {
+ let mut guard = x.write();
+ *guard += 1;
+ }
+ baz().await;
+ let guard = x.read();
+ 47
+ }
+
+ pub async fn also_bad(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let guard = x.lock();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+ }
+
+ pub async fn not_good(x: &Mutex<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let guard = x.lock();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+ }
+
+ #[allow(clippy::manual_async_fn)]
+ pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let guard = x.lock();
+ baz().await
+ }
+ }
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+async fn no_await(x: std::sync::Mutex<u32>) {
+ let mut guard = x.lock().unwrap();
+ *guard += 1;
+}
+
+// FIXME: FP, because the `MutexGuard` is dropped before crossing the await point. This is
+// something the needs to be fixed in rustc. There's already drop-tracking, but this is currently
+// disabled, see rust-lang/rust#93751. This case isn't picked up by drop-tracking though. If the
+// `*guard += 1` is removed it is picked up.
+async fn dropped_before_await(x: std::sync::Mutex<u32>) {
+ let mut guard = x.lock().unwrap();
+ *guard += 1;
+ drop(guard);
+ baz().await;
+}
+
+fn main() {
+ let m = std::sync::Mutex::new(100);
+ std_mutex::good(&m);
+ std_mutex::bad(&m);
+ std_mutex::also_bad(&m);
+ std_mutex::not_good(&m);
+ std_mutex::block_bad(&m);
+
+ let m = parking_lot::Mutex::new(100);
+ parking_lot_mutex::good(&m);
+ parking_lot_mutex::bad(&m);
+ parking_lot_mutex::also_bad(&m);
+ parking_lot_mutex::not_good(&m);
+}
diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr
new file mode 100644
index 000000000..976da8d92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr
@@ -0,0 +1,208 @@
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:9:13
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+ = note: `-D clippy::await-holding-lock` implied by `-D warnings`
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:9:9
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:24:13
+ |
+LL | let guard = x.read().unwrap();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:24:9
+ |
+LL | / let guard = x.read().unwrap();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:29:13
+ |
+LL | let mut guard = x.write().unwrap();
+ | ^^^^^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:29:9
+ |
+LL | / let mut guard = x.write().unwrap();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:50:13
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:50:9
+ |
+LL | / let guard = x.lock().unwrap();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:63:17
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:63:13
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | };
+ | |_________^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:75:17
+ |
+LL | let guard = x.lock().unwrap();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:75:13
+ |
+LL | / let guard = x.lock().unwrap();
+LL | | baz().await
+LL | | }
+ | |_________^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:87:13
+ |
+LL | let guard = x.lock();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:87:9
+ |
+LL | / let guard = x.lock();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:102:13
+ |
+LL | let guard = x.read();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:102:9
+ |
+LL | / let guard = x.read();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:107:13
+ |
+LL | let mut guard = x.write();
+ | ^^^^^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:107:9
+ |
+LL | / let mut guard = x.write();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:128:13
+ |
+LL | let guard = x.lock();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:128:9
+ |
+LL | / let guard = x.lock();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_____^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:141:17
+ |
+LL | let guard = x.lock();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:141:13
+ |
+LL | / let guard = x.lock();
+LL | | baz().await
+LL | | };
+ | |_________^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:153:17
+ |
+LL | let guard = x.lock();
+ | ^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:153:13
+ |
+LL | / let guard = x.lock();
+LL | | baz().await
+LL | | }
+ | |_________^
+
+error: this `MutexGuard` is held across an `await` point
+ --> $DIR/await_holding_lock.rs:173:9
+ |
+LL | let mut guard = x.lock().unwrap();
+ | ^^^^^^^^^
+ |
+ = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
+note: these are all the `await` points this lock is held through
+ --> $DIR/await_holding_lock.rs:173:5
+ |
+LL | / let mut guard = x.lock().unwrap();
+LL | | *guard += 1;
+LL | | drop(guard);
+LL | | baz().await;
+LL | | }
+ | |_^
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
new file mode 100644
index 000000000..23b7095de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
@@ -0,0 +1,85 @@
+#![warn(clippy::await_holding_refcell_ref)]
+
+use std::cell::RefCell;
+
+async fn bad(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow();
+ baz().await
+}
+
+async fn bad_mut(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow_mut();
+ baz().await
+}
+
+async fn good(x: &RefCell<u32>) -> u32 {
+ {
+ let b = x.borrow_mut();
+ let y = *b + 1;
+ }
+ baz().await;
+ let b = x.borrow_mut();
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+async fn also_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn less_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ drop(b);
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn not_good(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let b = x.borrow_mut();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let b = x.borrow_mut();
+ baz().await
+ }
+}
+
+fn main() {
+ let rc = RefCell::new(100);
+ good(&rc);
+ bad(&rc);
+ bad_mut(&rc);
+ also_bad(&rc);
+ less_bad(&rc);
+ not_good(&rc);
+ block_bad(&rc);
+}
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
new file mode 100644
index 000000000..4339fca73
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
@@ -0,0 +1,101 @@
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:6:9
+ |
+LL | let b = x.borrow();
+ | ^
+ |
+ = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:6:5
+ |
+LL | / let b = x.borrow();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:11:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:11:5
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:32:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:32:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:44:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:44:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:59:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:59:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | };
+ | |_____^
+
+error: this `RefCell` reference is held across an `await` point
+ --> $DIR/await_holding_refcell_ref.rs:71:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+ = help: ensure the reference is dropped before calling `await`
+note: these are all the `await` points this reference is held through
+ --> $DIR/await_holding_refcell_ref.rs:71:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed
new file mode 100644
index 000000000..5815550d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![deny(clippy::bind_instead_of_map)]
+
+// need a main anyway, use it get rid of unused warnings too
+pub fn main() {
+ let x = Some(5);
+ // the easiest cases
+ let _ = x;
+ let _ = x.map(|o| o + 1);
+ // and an easy counter-example
+ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+
+ // Different type
+ let x: Result<u32, &str> = Ok(1);
+ let _ = x;
+}
+
+pub fn foo() -> Option<String> {
+ let x = Some(String::from("hello"));
+ Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
+}
+
+pub fn example2(x: bool) -> Option<&'static str> {
+ Some("a").and_then(|s| Some(if x { s } else { return None }))
+}
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.rs b/src/tools/clippy/tests/ui/bind_instead_of_map.rs
new file mode 100644
index 000000000..623b100a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![deny(clippy::bind_instead_of_map)]
+
+// need a main anyway, use it get rid of unused warnings too
+pub fn main() {
+ let x = Some(5);
+ // the easiest cases
+ let _ = x.and_then(Some);
+ let _ = x.and_then(|o| Some(o + 1));
+ // and an easy counter-example
+ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+
+ // Different type
+ let x: Result<u32, &str> = Ok(1);
+ let _ = x.and_then(Ok);
+}
+
+pub fn foo() -> Option<String> {
+ let x = Some(String::from("hello"));
+ Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
+}
+
+pub fn example2(x: bool) -> Option<&'static str> {
+ Some("a").and_then(|s| Some(if x { s } else { return None }))
+}
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr
new file mode 100644
index 000000000..24c6b7f9e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr
@@ -0,0 +1,26 @@
+error: using `Option.and_then(Some)`, which is a no-op
+ --> $DIR/bind_instead_of_map.rs:8:13
+ |
+LL | let _ = x.and_then(Some);
+ | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x`
+ |
+note: the lint level is defined here
+ --> $DIR/bind_instead_of_map.rs:2:9
+ |
+LL | #![deny(clippy::bind_instead_of_map)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
+ --> $DIR/bind_instead_of_map.rs:9:13
+ |
+LL | let _ = x.and_then(|o| Some(o + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)`
+
+error: using `Result.and_then(Ok)`, which is a no-op
+ --> $DIR/bind_instead_of_map.rs:15:13
+ |
+LL | let _ = x.and_then(Ok);
+ | ^^^^^^^^^^^^^^ help: use the expression directly: `x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
new file mode 100644
index 000000000..e15898432
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
@@ -0,0 +1,62 @@
+// run-rustfix
+#![deny(clippy::bind_instead_of_map)]
+#![allow(clippy::blocks_in_if_conditions)]
+
+pub fn main() {
+ let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+ let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) });
+
+ let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+ let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) });
+
+ let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
+ let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) });
+
+ hard_example();
+ macro_example();
+}
+
+fn hard_example() {
+ Some("42").map(|s| {
+ if {
+ if s == "43" {
+ return 43;
+ }
+ s == "42"
+ } {
+ return 45;
+ }
+ match s.len() {
+ 10 => 2,
+ 20 => {
+ if foo() {
+ return {
+ if foo() {
+ return 20;
+ }
+ println!("foo");
+ 3
+ };
+ }
+ 20
+ },
+ 40 => 30,
+ _ => 1,
+ }
+ });
+}
+
+fn foo() -> bool {
+ true
+}
+
+macro_rules! m {
+ () => {
+ Some(10)
+ };
+}
+
+fn macro_example() {
+ let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) });
+ let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
+}
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
new file mode 100644
index 000000000..49944403f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
@@ -0,0 +1,62 @@
+// run-rustfix
+#![deny(clippy::bind_instead_of_map)]
+#![allow(clippy::blocks_in_if_conditions)]
+
+pub fn main() {
+ let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
+ let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) });
+
+ let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
+ let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) });
+
+ let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
+ let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) });
+
+ hard_example();
+ macro_example();
+}
+
+fn hard_example() {
+ Some("42").and_then(|s| {
+ if {
+ if s == "43" {
+ return Some(43);
+ }
+ s == "42"
+ } {
+ return Some(45);
+ }
+ match s.len() {
+ 10 => Some(2),
+ 20 => {
+ if foo() {
+ return {
+ if foo() {
+ return Some(20);
+ }
+ println!("foo");
+ Some(3)
+ };
+ }
+ Some(20)
+ },
+ 40 => Some(30),
+ _ => Some(1),
+ }
+ });
+}
+
+fn foo() -> bool {
+ true
+}
+
+macro_rules! m {
+ () => {
+ Some(10)
+ };
+}
+
+fn macro_example() {
+ let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) });
+ let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
+}
diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
new file mode 100644
index 000000000..0152a93fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
@@ -0,0 +1,91 @@
+error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
+ --> $DIR/bind_instead_of_map_multipart.rs:6:13
+ |
+LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/bind_instead_of_map_multipart.rs:2:9
+ |
+LL | #![deny(clippy::bind_instead_of_map)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try this
+ |
+LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+ | ~~~ ~ ~~~~~~~
+
+error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)`
+ --> $DIR/bind_instead_of_map_multipart.rs:9:13
+ |
+LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
+ | ~~~ ~ ~~~~~~~
+
+error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
+ --> $DIR/bind_instead_of_map_multipart.rs:12:13
+ |
+LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
+ | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~
+
+error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
+ --> $DIR/bind_instead_of_map_multipart.rs:20:5
+ |
+LL | / Some("42").and_then(|s| {
+LL | | if {
+LL | | if s == "43" {
+LL | | return Some(43);
+... |
+LL | | }
+LL | | });
+ | |______^
+ |
+help: try this
+ |
+LL ~ Some("42").map(|s| {
+LL | if {
+LL | if s == "43" {
+LL ~ return 43;
+LL | }
+LL | s == "42"
+LL | } {
+LL ~ return 45;
+LL | }
+LL | match s.len() {
+LL ~ 10 => 2,
+LL | 20 => {
+ ...
+LL | if foo() {
+LL ~ return 20;
+LL | }
+LL | println!("foo");
+LL ~ 3
+LL | };
+LL | }
+LL ~ 20
+LL | },
+LL ~ 40 => 30,
+LL ~ _ => 1,
+ |
+
+error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
+ --> $DIR/bind_instead_of_map_multipart.rs:61:13
+ |
+LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
+ | ~~~ ~~~~ ~~~~~~~~
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bit_masks.rs b/src/tools/clippy/tests/ui/bit_masks.rs
new file mode 100644
index 000000000..cfb493fb5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bit_masks.rs
@@ -0,0 +1,63 @@
+const THREE_BITS: i64 = 7;
+const EVEN_MORE_REDIRECTION: i64 = THREE_BITS;
+
+#[warn(clippy::bad_bit_mask)]
+#[allow(
+ clippy::ineffective_bit_mask,
+ clippy::identity_op,
+ clippy::no_effect,
+ clippy::unnecessary_operation
+)]
+fn main() {
+ let x = 5;
+
+ x & 0 == 0;
+ x & 1 == 1; //ok, distinguishes bit 0
+ x & 1 == 0; //ok, compared with zero
+ x & 2 == 1;
+ x | 0 == 0; //ok, equals x == 0 (maybe warn?)
+ x | 1 == 3; //ok, equals x == 2 || x == 3
+ x | 3 == 3; //ok, equals x <= 3
+ x | 3 == 2;
+
+ x & 1 > 1;
+ x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
+ x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
+ x | 1 > 1; // ok (if a bit silly), equals x > 1
+ x | 2 > 1;
+ x | 2 <= 2; // ok (if a bit silly), equals x <= 2
+
+ x & 192 == 128; // ok, tests for bit 7 and not bit 6
+ x & 0xffc0 == 0xfe80; // ok
+
+ // this also now works with constants
+ x & THREE_BITS == 8;
+ x | EVEN_MORE_REDIRECTION < 7;
+
+ 0 & x == 0;
+ 1 | x > 1;
+
+ // and should now also match uncommon usage
+ 1 < 2 | x;
+ 2 == 3 | x;
+ 1 == x & 2;
+
+ x | 1 > 2; // no error, because we allowed ineffective bit masks
+ ineffective();
+}
+
+#[warn(clippy::ineffective_bit_mask)]
+#[allow(clippy::bad_bit_mask, clippy::no_effect, clippy::unnecessary_operation)]
+fn ineffective() {
+ let x = 5;
+
+ x | 1 > 3;
+ x | 1 < 4;
+ x | 1 <= 3;
+ x | 1 >= 8;
+
+ x | 1 > 2; // not an error (yet), better written as x >= 2
+ x | 1 >= 7; // not an error (yet), better written as x >= 6
+ x | 3 > 4; // not an error (yet), better written as x >= 4
+ x | 4 <= 19;
+}
diff --git a/src/tools/clippy/tests/ui/bit_masks.stderr b/src/tools/clippy/tests/ui/bit_masks.stderr
new file mode 100644
index 000000000..dc5ad6dfb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bit_masks.stderr
@@ -0,0 +1,110 @@
+error: &-masking with zero
+ --> $DIR/bit_masks.rs:14:5
+ |
+LL | x & 0 == 0;
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::bad-bit-mask` implied by `-D warnings`
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/bit_masks.rs:14:5
+ |
+LL | x & 0 == 0;
+ | ^^^^^
+ |
+ = note: `#[deny(clippy::erasing_op)]` on by default
+
+error: incompatible bit mask: `_ & 2` can never be equal to `1`
+ --> $DIR/bit_masks.rs:17:5
+ |
+LL | x & 2 == 1;
+ | ^^^^^^^^^^
+
+error: incompatible bit mask: `_ | 3` can never be equal to `2`
+ --> $DIR/bit_masks.rs:21:5
+ |
+LL | x | 3 == 2;
+ | ^^^^^^^^^^
+
+error: incompatible bit mask: `_ & 1` will never be higher than `1`
+ --> $DIR/bit_masks.rs:23:5
+ |
+LL | x & 1 > 1;
+ | ^^^^^^^^^
+
+error: incompatible bit mask: `_ | 2` will always be higher than `1`
+ --> $DIR/bit_masks.rs:27:5
+ |
+LL | x | 2 > 1;
+ | ^^^^^^^^^
+
+error: incompatible bit mask: `_ & 7` can never be equal to `8`
+ --> $DIR/bit_masks.rs:34:5
+ |
+LL | x & THREE_BITS == 8;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: incompatible bit mask: `_ | 7` will never be lower than `7`
+ --> $DIR/bit_masks.rs:35:5
+ |
+LL | x | EVEN_MORE_REDIRECTION < 7;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: &-masking with zero
+ --> $DIR/bit_masks.rs:37:5
+ |
+LL | 0 & x == 0;
+ | ^^^^^^^^^^
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/bit_masks.rs:37:5
+ |
+LL | 0 & x == 0;
+ | ^^^^^
+
+error: incompatible bit mask: `_ | 2` will always be higher than `1`
+ --> $DIR/bit_masks.rs:41:5
+ |
+LL | 1 < 2 | x;
+ | ^^^^^^^^^
+
+error: incompatible bit mask: `_ | 3` can never be equal to `2`
+ --> $DIR/bit_masks.rs:42:5
+ |
+LL | 2 == 3 | x;
+ | ^^^^^^^^^^
+
+error: incompatible bit mask: `_ & 2` can never be equal to `1`
+ --> $DIR/bit_masks.rs:43:5
+ |
+LL | 1 == x & 2;
+ | ^^^^^^^^^^
+
+error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
+ --> $DIR/bit_masks.rs:54:5
+ |
+LL | x | 1 > 3;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::ineffective-bit-mask` implied by `-D warnings`
+
+error: ineffective bit mask: `x | 1` compared to `4`, is the same as x compared directly
+ --> $DIR/bit_masks.rs:55:5
+ |
+LL | x | 1 < 4;
+ | ^^^^^^^^^
+
+error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
+ --> $DIR/bit_masks.rs:56:5
+ |
+LL | x | 1 <= 3;
+ | ^^^^^^^^^^
+
+error: ineffective bit mask: `x | 1` compared to `8`, is the same as x compared directly
+ --> $DIR/bit_masks.rs:57:5
+ |
+LL | x | 1 >= 8;
+ | ^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/blacklisted_name.rs b/src/tools/clippy/tests/ui/blacklisted_name.rs
new file mode 100644
index 000000000..27df732a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blacklisted_name.rs
@@ -0,0 +1,57 @@
+#![allow(
+ dead_code,
+ clippy::similar_names,
+ clippy::single_match,
+ clippy::toplevel_ref_arg,
+ unused_mut,
+ unused_variables
+)]
+#![warn(clippy::blacklisted_name)]
+
+fn test(foo: ()) {}
+
+fn main() {
+ let foo = 42;
+ let baz = 42;
+ let quux = 42;
+ // Unlike these others, `bar` is actually considered an acceptable name.
+ // Among many other legitimate uses, bar commonly refers to a period of time in music.
+ // See https://github.com/rust-lang/rust-clippy/issues/5225.
+ let bar = 42;
+
+ let food = 42;
+ let foodstuffs = 42;
+ let bazaar = 42;
+
+ match (42, Some(1337), Some(0)) {
+ (foo, Some(baz), quux @ Some(_)) => (),
+ _ => (),
+ }
+}
+
+fn issue_1647(mut foo: u8) {
+ let mut baz = 0;
+ if let Some(mut quux) = Some(42) {}
+}
+
+fn issue_1647_ref() {
+ let ref baz = 0;
+ if let Some(ref quux) = Some(42) {}
+}
+
+fn issue_1647_ref_mut() {
+ let ref mut baz = 0;
+ if let Some(ref mut quux) = Some(42) {}
+}
+
+mod tests {
+ fn issue_7305() {
+ // `blacklisted_name` lint should not be triggered inside of the test code.
+ let foo = 0;
+
+ // Check that even in nested functions warning is still not triggered.
+ fn nested() {
+ let foo = 0;
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/blacklisted_name.stderr b/src/tools/clippy/tests/ui/blacklisted_name.stderr
new file mode 100644
index 000000000..70dbdaece
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blacklisted_name.stderr
@@ -0,0 +1,88 @@
+error: use of a blacklisted/placeholder name `foo`
+ --> $DIR/blacklisted_name.rs:11:9
+ |
+LL | fn test(foo: ()) {}
+ | ^^^
+ |
+ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
+
+error: use of a blacklisted/placeholder name `foo`
+ --> $DIR/blacklisted_name.rs:14:9
+ |
+LL | let foo = 42;
+ | ^^^
+
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:15:9
+ |
+LL | let baz = 42;
+ | ^^^
+
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:16:9
+ |
+LL | let quux = 42;
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `foo`
+ --> $DIR/blacklisted_name.rs:27:10
+ |
+LL | (foo, Some(baz), quux @ Some(_)) => (),
+ | ^^^
+
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:27:20
+ |
+LL | (foo, Some(baz), quux @ Some(_)) => (),
+ | ^^^
+
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:27:26
+ |
+LL | (foo, Some(baz), quux @ Some(_)) => (),
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `foo`
+ --> $DIR/blacklisted_name.rs:32:19
+ |
+LL | fn issue_1647(mut foo: u8) {
+ | ^^^
+
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:33:13
+ |
+LL | let mut baz = 0;
+ | ^^^
+
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:34:21
+ |
+LL | if let Some(mut quux) = Some(42) {}
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:38:13
+ |
+LL | let ref baz = 0;
+ | ^^^
+
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:39:21
+ |
+LL | if let Some(ref quux) = Some(42) {}
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `baz`
+ --> $DIR/blacklisted_name.rs:43:17
+ |
+LL | let ref mut baz = 0;
+ | ^^^
+
+error: use of a blacklisted/placeholder name `quux`
+ --> $DIR/blacklisted_name.rs:44:25
+ |
+LL | if let Some(ref mut quux) = Some(42) {}
+ | ^^^^
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.rs b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.rs
new file mode 100644
index 000000000..d055f1752
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::blanket_clippy_restriction_lints)]
+
+//! Test that the whole restriction group is not enabled
+#![warn(clippy::restriction)]
+#![deny(clippy::restriction)]
+#![forbid(clippy::restriction)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
new file mode 100644
index 000000000..537557f8b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
@@ -0,0 +1,27 @@
+error: restriction lints are not meant to be all enabled
+ --> $DIR/blanket_clippy_restriction_lints.rs:4:9
+ |
+LL | #![warn(clippy::restriction)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
+ = help: try enabling only the lints you really need
+
+error: restriction lints are not meant to be all enabled
+ --> $DIR/blanket_clippy_restriction_lints.rs:5:9
+ |
+LL | #![deny(clippy::restriction)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try enabling only the lints you really need
+
+error: restriction lints are not meant to be all enabled
+ --> $DIR/blanket_clippy_restriction_lints.rs:6:11
+ |
+LL | #![forbid(clippy::restriction)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try enabling only the lints you really need
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
new file mode 100644
index 000000000..e6e40a994
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed
@@ -0,0 +1,65 @@
+// run-rustfix
+#![warn(clippy::blocks_in_if_conditions)]
+#![allow(unused, clippy::let_and_return)]
+#![warn(clippy::nonminimal_bool)]
+
+macro_rules! blocky {
+ () => {{ true }};
+}
+
+macro_rules! blocky_too {
+ () => {{
+ let r = true;
+ r
+ }};
+}
+
+fn macro_if() {
+ if blocky!() {}
+
+ if blocky_too!() {}
+}
+
+fn condition_has_block() -> i32 {
+ let res = {
+ let x = 3;
+ x == 3
+ }; if res {
+ 6
+ } else {
+ 10
+ }
+}
+
+fn condition_has_block_with_single_expression() -> i32 {
+ if true { 6 } else { 10 }
+}
+
+fn condition_is_normal() -> i32 {
+ let x = 3;
+ if x == 3 { 6 } else { 10 }
+}
+
+fn condition_is_unsafe_block() {
+ let a: i32 = 1;
+
+ // this should not warn because the condition is an unsafe block
+ if unsafe { 1u32 == std::mem::transmute(a) } {
+ println!("1u32 == a");
+ }
+}
+
+fn block_in_assert() {
+ let opt = Some(42);
+ assert!(
+ opt.as_ref()
+ .map(|val| {
+ let mut v = val * 2;
+ v -= 1;
+ v * 3
+ })
+ .is_some()
+ );
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
new file mode 100644
index 000000000..69387ff57
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs
@@ -0,0 +1,65 @@
+// run-rustfix
+#![warn(clippy::blocks_in_if_conditions)]
+#![allow(unused, clippy::let_and_return)]
+#![warn(clippy::nonminimal_bool)]
+
+macro_rules! blocky {
+ () => {{ true }};
+}
+
+macro_rules! blocky_too {
+ () => {{
+ let r = true;
+ r
+ }};
+}
+
+fn macro_if() {
+ if blocky!() {}
+
+ if blocky_too!() {}
+}
+
+fn condition_has_block() -> i32 {
+ if {
+ let x = 3;
+ x == 3
+ } {
+ 6
+ } else {
+ 10
+ }
+}
+
+fn condition_has_block_with_single_expression() -> i32 {
+ if { true } { 6 } else { 10 }
+}
+
+fn condition_is_normal() -> i32 {
+ let x = 3;
+ if true && x == 3 { 6 } else { 10 }
+}
+
+fn condition_is_unsafe_block() {
+ let a: i32 = 1;
+
+ // this should not warn because the condition is an unsafe block
+ if unsafe { 1u32 == std::mem::transmute(a) } {
+ println!("1u32 == a");
+ }
+}
+
+fn block_in_assert() {
+ let opt = Some(42);
+ assert!(
+ opt.as_ref()
+ .map(|val| {
+ let mut v = val * 2;
+ v -= 1;
+ v * 3
+ })
+ .is_some()
+ );
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr
new file mode 100644
index 000000000..079f2feb5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr
@@ -0,0 +1,34 @@
+error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+ --> $DIR/blocks_in_if_conditions.rs:24:5
+ |
+LL | / if {
+LL | | let x = 3;
+LL | | x == 3
+LL | | } {
+ | |_____^
+ |
+ = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
+help: try
+ |
+LL ~ let res = {
+LL + let x = 3;
+LL + x == 3
+LL ~ }; if res {
+ |
+
+error: omit braces around single expression condition
+ --> $DIR/blocks_in_if_conditions.rs:35:8
+ |
+LL | if { true } { 6 } else { 10 }
+ | ^^^^^^^^ help: try: `true`
+
+error: this boolean expression can be simplified
+ --> $DIR/blocks_in_if_conditions.rs:40:8
+ |
+LL | if true && x == 3 { 6 } else { 10 }
+ | ^^^^^^^^^^^^^^ help: try: `x == 3`
+ |
+ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
new file mode 100644
index 000000000..169589f6d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
@@ -0,0 +1,64 @@
+#![warn(clippy::blocks_in_if_conditions)]
+#![allow(unused, clippy::let_and_return)]
+
+fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool {
+ pfn(val)
+}
+
+fn pred_test() {
+ let v = 3;
+ let sky = "blue";
+ // This is a sneaky case, where the block isn't directly in the condition,
+ // but is actually inside a closure that the condition is using.
+ // The same principle applies -- add some extra expressions to make sure
+ // linter isn't confused by them.
+ if v == 3
+ && sky == "blue"
+ && predicate(
+ |x| {
+ let target = 3;
+ x == target
+ },
+ v,
+ )
+ {}
+
+ if predicate(
+ |x| {
+ let target = 3;
+ x == target
+ },
+ v,
+ ) {}
+}
+
+fn closure_without_block() {
+ if predicate(|x| x == 3, 6) {}
+}
+
+fn macro_in_closure() {
+ let option = Some(true);
+
+ if option.unwrap_or_else(|| unimplemented!()) {
+ unimplemented!()
+ }
+}
+
+fn closure(_: impl FnMut()) -> bool {
+ true
+}
+
+fn function_with_empty_closure() {
+ if closure(|| {}) {}
+}
+
+#[rustfmt::skip]
+fn main() {
+ let mut range = 0..10;
+ range.all(|i| {i < 10} );
+
+ let v = vec![1, 2, 3];
+ if v.into_iter().any(|x| {x == 4}) {
+ println!("contains 4!");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
new file mode 100644
index 000000000..941d604dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.stderr
@@ -0,0 +1,24 @@
+error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+ --> $DIR/blocks_in_if_conditions_closure.rs:18:17
+ |
+LL | |x| {
+ | _________________^
+LL | | let target = 3;
+LL | | x == target
+LL | | },
+ | |_____________^
+ |
+ = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings`
+
+error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
+ --> $DIR/blocks_in_if_conditions_closure.rs:27:13
+ |
+LL | |x| {
+ | _____________^
+LL | | let target = 3;
+LL | | x == target
+LL | | },
+ | |_________^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
new file mode 100644
index 000000000..ec4d6f3ff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
@@ -0,0 +1,122 @@
+#![warn(clippy::bool_assert_comparison)]
+
+use std::ops::Not;
+
+macro_rules! a {
+ () => {
+ true
+ };
+}
+macro_rules! b {
+ () => {
+ true
+ };
+}
+
+// Implements the Not trait but with an output type
+// that's not bool. Should not suggest a rewrite
+#[derive(Debug)]
+enum ImplNotTraitWithoutBool {
+ VariantX(bool),
+ VariantY(u32),
+}
+
+impl PartialEq<bool> for ImplNotTraitWithoutBool {
+ fn eq(&self, other: &bool) -> bool {
+ match *self {
+ ImplNotTraitWithoutBool::VariantX(b) => b == *other,
+ _ => false,
+ }
+ }
+}
+
+impl Not for ImplNotTraitWithoutBool {
+ type Output = Self;
+
+ fn not(self) -> Self::Output {
+ match self {
+ ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b),
+ ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1),
+ ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0),
+ }
+ }
+}
+
+// This type implements the Not trait with an Output of
+// type bool. Using assert!(..) must be suggested
+#[derive(Debug)]
+struct ImplNotTraitWithBool;
+
+impl PartialEq<bool> for ImplNotTraitWithBool {
+ fn eq(&self, other: &bool) -> bool {
+ false
+ }
+}
+
+impl Not for ImplNotTraitWithBool {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ true
+ }
+}
+
+fn main() {
+ let a = ImplNotTraitWithoutBool::VariantX(true);
+ let b = ImplNotTraitWithBool;
+
+ assert_eq!("a".len(), 1);
+ assert_eq!("a".is_empty(), false);
+ assert_eq!("".is_empty(), true);
+ assert_eq!(true, "".is_empty());
+ assert_eq!(a!(), b!());
+ assert_eq!(a!(), "".is_empty());
+ assert_eq!("".is_empty(), b!());
+ assert_eq!(a, true);
+ assert_eq!(b, true);
+
+ assert_ne!("a".len(), 1);
+ assert_ne!("a".is_empty(), false);
+ assert_ne!("".is_empty(), true);
+ assert_ne!(true, "".is_empty());
+ assert_ne!(a!(), b!());
+ assert_ne!(a!(), "".is_empty());
+ assert_ne!("".is_empty(), b!());
+ assert_ne!(a, true);
+ assert_ne!(b, true);
+
+ debug_assert_eq!("a".len(), 1);
+ debug_assert_eq!("a".is_empty(), false);
+ debug_assert_eq!("".is_empty(), true);
+ debug_assert_eq!(true, "".is_empty());
+ debug_assert_eq!(a!(), b!());
+ debug_assert_eq!(a!(), "".is_empty());
+ debug_assert_eq!("".is_empty(), b!());
+ debug_assert_eq!(a, true);
+ debug_assert_eq!(b, true);
+
+ debug_assert_ne!("a".len(), 1);
+ debug_assert_ne!("a".is_empty(), false);
+ debug_assert_ne!("".is_empty(), true);
+ debug_assert_ne!(true, "".is_empty());
+ debug_assert_ne!(a!(), b!());
+ debug_assert_ne!(a!(), "".is_empty());
+ debug_assert_ne!("".is_empty(), b!());
+ debug_assert_ne!(a, true);
+ debug_assert_ne!(b, true);
+
+ // assert with error messages
+ assert_eq!("a".len(), 1, "tadam {}", 1);
+ assert_eq!("a".len(), 1, "tadam {}", true);
+ assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ assert_eq!("a".is_empty(), false, "tadam {}", true);
+ assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ assert_eq!(a, true, "tadam {}", false);
+
+ debug_assert_eq!("a".len(), 1, "tadam {}", 1);
+ debug_assert_eq!("a".len(), 1, "tadam {}", true);
+ debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+ debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ debug_assert_eq!(a, true, "tadam {}", false);
+}
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
new file mode 100644
index 000000000..377d51be4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
@@ -0,0 +1,136 @@
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:69:5
+ |
+LL | assert_eq!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ |
+ = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:70:5
+ |
+LL | assert_eq!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:71:5
+ |
+LL | assert_eq!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:76:5
+ |
+LL | assert_eq!(b, true);
+ | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:79:5
+ |
+LL | assert_ne!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:80:5
+ |
+LL | assert_ne!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:81:5
+ |
+LL | assert_ne!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:86:5
+ |
+LL | assert_ne!(b, true);
+ | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:89:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:90:5
+ |
+LL | debug_assert_eq!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:91:5
+ |
+LL | debug_assert_eq!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:96:5
+ |
+LL | debug_assert_eq!(b, true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:99:5
+ |
+LL | debug_assert_ne!("a".is_empty(), false);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:100:5
+ |
+LL | debug_assert_ne!("".is_empty(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:101:5
+ |
+LL | debug_assert_ne!(true, "".is_empty());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:106:5
+ |
+LL | debug_assert_ne!(b, true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:111:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:112:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:113:5
+ |
+LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:118:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:119:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:120:5
+ |
+LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed
new file mode 100644
index 000000000..5a012ff4d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_comparison.fixed
@@ -0,0 +1,167 @@
+// run-rustfix
+
+#![warn(clippy::bool_comparison)]
+
+fn main() {
+ let x = true;
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x {
+ "yes"
+ } else {
+ "no"
+ };
+ if !x {
+ "yes"
+ } else {
+ "no"
+ };
+ let y = true;
+ if !x & y {
+ "yes"
+ } else {
+ "no"
+ };
+ if x & !y {
+ "yes"
+ } else {
+ "no"
+ };
+}
+
+#[allow(dead_code)]
+fn issue3703() {
+ struct Foo;
+ impl PartialEq<bool> for Foo {
+ fn eq(&self, _: &bool) -> bool {
+ true
+ }
+ }
+ impl PartialEq<Foo> for bool {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+ }
+ impl PartialOrd<bool> for Foo {
+ fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
+ None
+ }
+ }
+ impl PartialOrd<Foo> for bool {
+ fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
+ None
+ }
+ }
+
+ if Foo == true {}
+ if true == Foo {}
+ if Foo != true {}
+ if true != Foo {}
+ if Foo == false {}
+ if false == Foo {}
+ if Foo != false {}
+ if false != Foo {}
+ if Foo < false {}
+ if false < Foo {}
+}
+
+#[allow(dead_code)]
+fn issue4983() {
+ let a = true;
+ let b = false;
+
+ if a != b {};
+ if a != b {};
+ if a == b {};
+ if !a == !b {};
+
+ if b != a {};
+ if b != a {};
+ if b == a {};
+ if !b == !a {};
+}
+
+macro_rules! m {
+ ($func:ident) => {
+ $func()
+ };
+}
+
+fn func() -> bool {
+ true
+}
+
+#[allow(dead_code)]
+fn issue3973() {
+ // ok, don't lint on `cfg` invocation
+ if false == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == false {}
+ if true == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == true {}
+
+ // lint, could be simplified
+ if !m!(func) {}
+ if !m!(func) {}
+ if m!(func) {}
+ if m!(func) {}
+
+ // no lint with a variable
+ let is_debug = false;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+ let is_debug = true;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs
new file mode 100644
index 000000000..c534bc25c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_comparison.rs
@@ -0,0 +1,167 @@
+// run-rustfix
+
+#![warn(clippy::bool_comparison)]
+
+fn main() {
+ let x = true;
+ if x == true {
+ "yes"
+ } else {
+ "no"
+ };
+ if x == false {
+ "yes"
+ } else {
+ "no"
+ };
+ if true == x {
+ "yes"
+ } else {
+ "no"
+ };
+ if false == x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x != true {
+ "yes"
+ } else {
+ "no"
+ };
+ if x != false {
+ "yes"
+ } else {
+ "no"
+ };
+ if true != x {
+ "yes"
+ } else {
+ "no"
+ };
+ if false != x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x < true {
+ "yes"
+ } else {
+ "no"
+ };
+ if false < x {
+ "yes"
+ } else {
+ "no"
+ };
+ if x > false {
+ "yes"
+ } else {
+ "no"
+ };
+ if true > x {
+ "yes"
+ } else {
+ "no"
+ };
+ let y = true;
+ if x < y {
+ "yes"
+ } else {
+ "no"
+ };
+ if x > y {
+ "yes"
+ } else {
+ "no"
+ };
+}
+
+#[allow(dead_code)]
+fn issue3703() {
+ struct Foo;
+ impl PartialEq<bool> for Foo {
+ fn eq(&self, _: &bool) -> bool {
+ true
+ }
+ }
+ impl PartialEq<Foo> for bool {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+ }
+ impl PartialOrd<bool> for Foo {
+ fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
+ None
+ }
+ }
+ impl PartialOrd<Foo> for bool {
+ fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
+ None
+ }
+ }
+
+ if Foo == true {}
+ if true == Foo {}
+ if Foo != true {}
+ if true != Foo {}
+ if Foo == false {}
+ if false == Foo {}
+ if Foo != false {}
+ if false != Foo {}
+ if Foo < false {}
+ if false < Foo {}
+}
+
+#[allow(dead_code)]
+fn issue4983() {
+ let a = true;
+ let b = false;
+
+ if a == !b {};
+ if !a == b {};
+ if a == b {};
+ if !a == !b {};
+
+ if b == !a {};
+ if !b == a {};
+ if b == a {};
+ if !b == !a {};
+}
+
+macro_rules! m {
+ ($func:ident) => {
+ $func()
+ };
+}
+
+fn func() -> bool {
+ true
+}
+
+#[allow(dead_code)]
+fn issue3973() {
+ // ok, don't lint on `cfg` invocation
+ if false == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == false {}
+ if true == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == true {}
+
+ // lint, could be simplified
+ if false == m!(func) {}
+ if m!(func) == false {}
+ if true == m!(func) {}
+ if m!(func) == true {}
+
+ // no lint with a variable
+ let is_debug = false;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+ let is_debug = true;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr
new file mode 100644
index 000000000..31522d4a5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_comparison.stderr
@@ -0,0 +1,136 @@
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:7:8
+ |
+LL | if x == true {
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+ |
+ = note: `-D clippy::bool-comparison` implied by `-D warnings`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:12:8
+ |
+LL | if x == false {
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:17:8
+ |
+LL | if true == x {
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:22:8
+ |
+LL | if false == x {
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: inequality checks against true can be replaced by a negation
+ --> $DIR/bool_comparison.rs:27:8
+ |
+LL | if x != true {
+ | ^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: inequality checks against false are unnecessary
+ --> $DIR/bool_comparison.rs:32:8
+ |
+LL | if x != false {
+ | ^^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: inequality checks against true can be replaced by a negation
+ --> $DIR/bool_comparison.rs:37:8
+ |
+LL | if true != x {
+ | ^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: inequality checks against false are unnecessary
+ --> $DIR/bool_comparison.rs:42:8
+ |
+LL | if false != x {
+ | ^^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: less than comparison against true can be replaced by a negation
+ --> $DIR/bool_comparison.rs:47:8
+ |
+LL | if x < true {
+ | ^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: greater than checks against false are unnecessary
+ --> $DIR/bool_comparison.rs:52:8
+ |
+LL | if false < x {
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: greater than checks against false are unnecessary
+ --> $DIR/bool_comparison.rs:57:8
+ |
+LL | if x > false {
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: less than comparison against true can be replaced by a negation
+ --> $DIR/bool_comparison.rs:62:8
+ |
+LL | if true > x {
+ | ^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: order comparisons between booleans can be simplified
+ --> $DIR/bool_comparison.rs:68:8
+ |
+LL | if x < y {
+ | ^^^^^ help: try simplifying it as shown: `!x & y`
+
+error: order comparisons between booleans can be simplified
+ --> $DIR/bool_comparison.rs:73:8
+ |
+LL | if x > y {
+ | ^^^^^ help: try simplifying it as shown: `x & !y`
+
+error: this comparison might be written more concisely
+ --> $DIR/bool_comparison.rs:121:8
+ |
+LL | if a == !b {};
+ | ^^^^^^^ help: try simplifying it as shown: `a != b`
+
+error: this comparison might be written more concisely
+ --> $DIR/bool_comparison.rs:122:8
+ |
+LL | if !a == b {};
+ | ^^^^^^^ help: try simplifying it as shown: `a != b`
+
+error: this comparison might be written more concisely
+ --> $DIR/bool_comparison.rs:126:8
+ |
+LL | if b == !a {};
+ | ^^^^^^^ help: try simplifying it as shown: `b != a`
+
+error: this comparison might be written more concisely
+ --> $DIR/bool_comparison.rs:127:8
+ |
+LL | if !b == a {};
+ | ^^^^^^^ help: try simplifying it as shown: `b != a`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:151:8
+ |
+LL | if false == m!(func) {}
+ | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:152:8
+ |
+LL | if m!(func) == false {}
+ | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:153:8
+ |
+LL | if true == m!(func) {}
+ | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:154:8
+ |
+LL | if m!(func) == true {}
+ | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
new file mode 100644
index 000000000..ff5c6a8c3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+
+fn main() {
+ let val = 1;
+ let _p = std::ptr::addr_of!(val);
+
+ let mut val_mut = 1;
+ let _p_mut = std::ptr::addr_of_mut!(val_mut);
+}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
new file mode 100644
index 000000000..0f62ec6ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+
+fn main() {
+ let val = 1;
+ let _p = &val as *const i32;
+
+ let mut val_mut = 1;
+ let _p_mut = &mut val_mut as *mut i32;
+}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
new file mode 100644
index 000000000..be1ed7330
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
@@ -0,0 +1,16 @@
+error: borrow as raw pointer
+ --> $DIR/borrow_as_ptr.rs:6:14
+ |
+LL | let _p = &val as *const i32;
+ | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)`
+ |
+ = note: `-D clippy::borrow-as-ptr` implied by `-D warnings`
+
+error: borrow as raw pointer
+ --> $DIR/borrow_as_ptr.rs:9:18
+ |
+LL | let _p_mut = &mut val_mut as *mut i32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed
new file mode 100644
index 000000000..eaba3b1c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed
@@ -0,0 +1,22 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let val = 1;
+ let _p = core::ptr::addr_of!(val);
+
+ let mut val_mut = 1;
+ let _p_mut = core::ptr::addr_of_mut!(val_mut);
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs
new file mode 100644
index 000000000..d83f9d1f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs
@@ -0,0 +1,22 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let val = 1;
+ let _p = &val as *const i32;
+
+ let mut val_mut = 1;
+ let _p_mut = &mut val_mut as *mut i32;
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr
new file mode 100644
index 000000000..84c8ba7d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr
@@ -0,0 +1,16 @@
+error: borrow as raw pointer
+ --> $DIR/borrow_as_ptr_no_std.rs:9:14
+ |
+LL | let _p = &val as *const i32;
+ | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)`
+ |
+ = note: `-D clippy::borrow-as-ptr` implied by `-D warnings`
+
+error: borrow as raw pointer
+ --> $DIR/borrow_as_ptr_no_std.rs:12:18
+ |
+LL | let _p_mut = &mut val_mut as *mut i32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_box.rs b/src/tools/clippy/tests/ui/borrow_box.rs
new file mode 100644
index 000000000..b606f773c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_box.rs
@@ -0,0 +1,115 @@
+#![deny(clippy::borrowed_box)]
+#![allow(clippy::blacklisted_name)]
+#![allow(unused_variables)]
+#![allow(dead_code)]
+
+use std::fmt::Display;
+
+pub fn test1(foo: &mut Box<bool>) {
+ // Although this function could be changed to "&mut bool",
+ // avoiding the Box, mutable references to boxes are not
+ // flagged by this lint.
+ //
+ // This omission is intentional: By passing a mutable Box,
+ // the memory location of the pointed-to object could be
+ // modified. By passing a mutable reference, the contents
+ // could change, but not the location.
+ println!("{:?}", foo)
+}
+
+pub fn test2() {
+ let foo: &Box<bool>;
+}
+
+struct Test3<'a> {
+ foo: &'a Box<bool>,
+}
+
+trait Test4 {
+ fn test4(a: &Box<bool>);
+}
+
+impl<'a> Test4 for Test3<'a> {
+ fn test4(a: &Box<bool>) {
+ unimplemented!();
+ }
+}
+
+use std::any::Any;
+
+pub fn test5(foo: &mut Box<dyn Any>) {
+ println!("{:?}", foo)
+}
+
+pub fn test6() {
+ let foo: &Box<dyn Any>;
+}
+
+struct Test7<'a> {
+ foo: &'a Box<dyn Any>,
+}
+
+trait Test8 {
+ fn test8(a: &Box<dyn Any>);
+}
+
+impl<'a> Test8 for Test7<'a> {
+ fn test8(a: &Box<dyn Any>) {
+ unimplemented!();
+ }
+}
+
+pub fn test9(foo: &mut Box<dyn Any + Send + Sync>) {
+ let _ = foo;
+}
+
+pub fn test10() {
+ let foo: &Box<dyn Any + Send + 'static>;
+}
+
+struct Test11<'a> {
+ foo: &'a Box<dyn Any + Send>,
+}
+
+trait Test12 {
+ fn test4(a: &Box<dyn Any + 'static>);
+}
+
+impl<'a> Test12 for Test11<'a> {
+ fn test4(a: &Box<dyn Any + 'static>) {
+ unimplemented!();
+ }
+}
+
+pub fn test13(boxed_slice: &mut Box<[i32]>) {
+ // Unconditionally replaces the box pointer.
+ //
+ // This cannot be accomplished if "&mut [i32]" is passed,
+ // and provides a test case where passing a reference to
+ // a Box is valid.
+ let mut data = vec![12];
+ *boxed_slice = data.into_boxed_slice();
+}
+
+// The suggestion should include proper parentheses to avoid a syntax error.
+pub fn test14(_display: &Box<dyn Display>) {}
+pub fn test15(_display: &Box<dyn Display + Send>) {}
+pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
+
+pub fn test17(_display: &Box<impl Display>) {}
+pub fn test18(_display: &Box<impl Display + Send>) {}
+pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
+
+// This exists only to check what happens when parentheses are already present.
+// Even though the current implementation doesn't put extra parentheses,
+// it's fine that unnecessary parentheses appear in the future for some reason.
+pub fn test20(_display: &Box<(dyn Display + Send)>) {}
+
+fn main() {
+ test1(&mut Box::new(false));
+ test2();
+ test5(&mut (Box::new(false) as Box<dyn Any>));
+ test6();
+ test9(&mut (Box::new(false) as Box<dyn Any + Send + Sync>));
+ test10();
+}
diff --git a/src/tools/clippy/tests/ui/borrow_box.stderr b/src/tools/clippy/tests/ui/borrow_box.stderr
new file mode 100644
index 000000000..3eac32815
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_box.stderr
@@ -0,0 +1,68 @@
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:21:14
+ |
+LL | let foo: &Box<bool>;
+ | ^^^^^^^^^^ help: try: `&bool`
+ |
+note: the lint level is defined here
+ --> $DIR/borrow_box.rs:1:9
+ |
+LL | #![deny(clippy::borrowed_box)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:25:10
+ |
+LL | foo: &'a Box<bool>,
+ | ^^^^^^^^^^^^^ help: try: `&'a bool`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:29:17
+ |
+LL | fn test4(a: &Box<bool>);
+ | ^^^^^^^^^^ help: try: `&bool`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:95:25
+ |
+LL | pub fn test14(_display: &Box<dyn Display>) {}
+ | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:96:25
+ |
+LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:97:29
+ |
+LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:99:25
+ |
+LL | pub fn test17(_display: &Box<impl Display>) {}
+ | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:100:25
+ |
+LL | pub fn test18(_display: &Box<impl Display + Send>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:101:29
+ |
+LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:106:25
+ |
+LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
new file mode 100644
index 000000000..bf4691c5b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn main() {}
+
+mod should_lint {
+ fn one_help() {
+ let a = &12;
+ let b = a;
+
+ let b = &mut bar(&12);
+ }
+
+ fn bar(x: &u32) -> &u32 {
+ x
+ }
+}
+
+// this mod explains why we should not lint `&mut &* (&T)`
+mod should_not_lint1 {
+ fn foo(x: &mut &u32) {
+ *x = &1;
+ }
+
+ fn main() {
+ let mut x = &0;
+ foo(&mut &*x); // should not lint
+ assert_eq!(*x, 0);
+
+ foo(&mut x);
+ assert_eq!(*x, 1);
+ }
+}
+
+// similar to should_not_lint1
+mod should_not_lint2 {
+ struct S<'a> {
+ a: &'a u32,
+ b: u32,
+ }
+
+ fn main() {
+ let s = S { a: &1, b: 1 };
+ let x = &mut &*s.a;
+ *x = &2;
+ }
+}
+
+// this mod explains why we should not lint `& &* (&T)`
+mod false_negative {
+ fn foo() {
+ let x = &12;
+ let addr_x = &x as *const _ as usize;
+ let addr_y = &x as *const _ as usize; // assert ok
+ // let addr_y = &x as *const _ as usize; // assert fail
+ assert_ne!(addr_x, addr_y);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
new file mode 100644
index 000000000..28c005fdb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn main() {}
+
+mod should_lint {
+ fn one_help() {
+ let a = &12;
+ let b = &*a;
+
+ let b = &mut &*bar(&12);
+ }
+
+ fn bar(x: &u32) -> &u32 {
+ x
+ }
+}
+
+// this mod explains why we should not lint `&mut &* (&T)`
+mod should_not_lint1 {
+ fn foo(x: &mut &u32) {
+ *x = &1;
+ }
+
+ fn main() {
+ let mut x = &0;
+ foo(&mut &*x); // should not lint
+ assert_eq!(*x, 0);
+
+ foo(&mut x);
+ assert_eq!(*x, 1);
+ }
+}
+
+// similar to should_not_lint1
+mod should_not_lint2 {
+ struct S<'a> {
+ a: &'a u32,
+ b: u32,
+ }
+
+ fn main() {
+ let s = S { a: &1, b: 1 };
+ let x = &mut &*s.a;
+ *x = &2;
+ }
+}
+
+// this mod explains why we should not lint `& &* (&T)`
+mod false_negative {
+ fn foo() {
+ let x = &12;
+ let addr_x = &x as *const _ as usize;
+ let addr_y = &&*x as *const _ as usize; // assert ok
+ // let addr_y = &x as *const _ as usize; // assert fail
+ assert_ne!(addr_x, addr_y);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr
new file mode 100644
index 000000000..d72de37c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr
@@ -0,0 +1,22 @@
+error: deref on an immutable reference
+ --> $DIR/borrow_deref_ref.rs:10:17
+ |
+LL | let b = &*a;
+ | ^^^ help: if you would like to reborrow, try removing `&*`: `a`
+ |
+ = note: `-D clippy::borrow-deref-ref` implied by `-D warnings`
+
+error: deref on an immutable reference
+ --> $DIR/borrow_deref_ref.rs:12:22
+ |
+LL | let b = &mut &*bar(&12);
+ | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)`
+
+error: deref on an immutable reference
+ --> $DIR/borrow_deref_ref.rs:55:23
+ |
+LL | let addr_y = &&*x as *const _ as usize; // assert ok
+ | ^^^ help: if you would like to reborrow, try removing `&*`: `x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.rs b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.rs
new file mode 100644
index 000000000..a8e2bbfef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.rs
@@ -0,0 +1,10 @@
+#![allow(dead_code, unused_variables)]
+
+fn main() {}
+
+mod should_lint {
+ fn two_helps() {
+ let s = &String::new();
+ let x: &str = &*s;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
new file mode 100644
index 000000000..738b01e7e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
@@ -0,0 +1,18 @@
+error: deref on an immutable reference
+ --> $DIR/borrow_deref_ref_unfixable.rs:8:23
+ |
+LL | let x: &str = &*s;
+ | ^^^
+ |
+ = note: `-D clippy::borrow-deref-ref` implied by `-D warnings`
+help: if you would like to reborrow, try removing `&*`
+ |
+LL | let x: &str = s;
+ | ~
+help: if you would like to deref, try using `&**`
+ |
+LL | let x: &str = &**s;
+ | ~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
new file mode 100644
index 000000000..f13733af3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
@@ -0,0 +1,17 @@
+// this file solely exists to test constants defined in foreign crates.
+// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure.
+
+#![allow(clippy::declare_interior_mutable_const)]
+#![allow(unused_tuple_struct_fields)]
+
+use std::sync::atomic::AtomicUsize;
+
+enum Private<T> {
+ ToBeUnfrozen(T),
+ Frozen(usize),
+}
+
+pub struct Wrapper(Private<AtomicUsize>);
+
+pub const WRAPPED_PRIVATE_UNFROZEN_VARIANT: Wrapper = Wrapper(Private::ToBeUnfrozen(AtomicUsize::new(6)));
+pub const WRAPPED_PRIVATE_FROZEN_VARIANT: Wrapper = Wrapper(Private::Frozen(7));
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs
new file mode 100644
index 000000000..5027db445
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs
@@ -0,0 +1,101 @@
+// aux-build:helper.rs
+
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const)]
+
+// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions.
+
+extern crate helper;
+
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+enum OptionalCell {
+ Unfrozen(Cell<bool>),
+ Frozen,
+}
+
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+fn borrow_optional_cell() {
+ let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability
+ let _ = &FROZEN_VARIANT;
+}
+
+trait AssocConsts {
+ const TO_BE_UNFROZEN_VARIANT: OptionalCell;
+ const TO_BE_FROZEN_VARIANT: OptionalCell;
+
+ const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+ const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+ fn function() {
+ // This is the "suboptimal behavior" mentioned in `is_value_unfrozen`
+ // caused by a similar reason to unfrozen types without any default values
+ // get linted even if it has frozen variants'.
+ let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+
+ // The lint ignores default values because an impl of this trait can set
+ // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`.
+ let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+ }
+}
+
+impl AssocConsts for u64 {
+ const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+ const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+ fn function() {
+ let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT;
+ let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
+ }
+}
+
+trait AssocTypes {
+ type ToBeUnfrozen;
+
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+ const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+
+ // there's no need to test here because it's the exactly same as `trait::AssocTypes`
+ fn function();
+}
+
+impl AssocTypes for u64 {
+ type ToBeUnfrozen = AtomicUsize;
+
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+ const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
+
+ fn function() {
+ let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT;
+ }
+}
+
+enum BothOfCellAndGeneric<T> {
+ Unfrozen(Cell<*const T>),
+ Generic(*const T),
+ Frozen(usize),
+}
+
+impl<T> BothOfCellAndGeneric<T> {
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+ const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
+
+ fn function() {
+ let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
+ let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
+ let _ = &Self::FROZEN_VARIANT;
+ }
+}
+
+fn main() {
+ // constants defined in foreign crates
+ let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+ let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT;
+}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr
new file mode 100644
index 000000000..654a1ee7d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr
@@ -0,0 +1,75 @@
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:22:14
+ |
+LL | let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:37:18
+ |
+LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:41:18
+ |
+LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:50:18
+ |
+LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:52:18
+ |
+LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:74:18
+ |
+LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:91:18
+ |
+LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:92:18
+ |
+LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/enums.rs:99:14
+ |
+LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
new file mode 100644
index 000000000..eefeb1dec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
@@ -0,0 +1,104 @@
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
+#![allow(const_item_mutation)]
+
+use std::borrow::Cow;
+use std::cell::{Cell, UnsafeCell};
+use std::fmt::Display;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Once;
+
+const ATOMIC: AtomicUsize = AtomicUsize::new(5);
+const CELL: Cell<usize> = Cell::new(6);
+const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+const INTEGER: u8 = 8;
+const STRING: String = String::new();
+const STR: &str = "012345";
+const COW: Cow<str> = Cow::Borrowed("abcdef");
+const NO_ANN: &dyn Display = &70;
+static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
+const ONCE_INIT: Once = Once::new();
+
+// This is just a pointer that can be safely dereferenced,
+// it's semantically the same as `&'static T`;
+// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
+// For more information, please see the issue #5918.
+pub struct StaticRef<T> {
+ ptr: *const T,
+}
+
+impl<T> StaticRef<T> {
+ /// Create a new `StaticRef` from a raw pointer
+ ///
+ /// ## Safety
+ ///
+ /// Callers must pass in a reference to statically allocated memory which
+ /// does not overlap with other values.
+ pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
+ StaticRef { ptr }
+ }
+}
+
+impl<T> std::ops::Deref for StaticRef<T> {
+ type Target = T;
+
+ fn deref(&self) -> &'static T {
+ unsafe { &*self.ptr }
+ }
+}
+
+// use a tuple to make sure referencing a field behind a pointer isn't linted.
+const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
+
+fn main() {
+ ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
+ assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
+
+ let _once = ONCE_INIT;
+ let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
+ let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
+ let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
+ let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
+ let _atomic_into_inner = ATOMIC.into_inner();
+ // these should be all fine.
+ let _twice = (ONCE_INIT, ONCE_INIT);
+ let _ref_twice = &(ONCE_INIT, ONCE_INIT);
+ let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
+ let _array_twice = [ONCE_INIT, ONCE_INIT];
+ let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
+ let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
+
+ // referencing projection is still bad.
+ let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
+ let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
+ let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
+ let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+ let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
+ let _ = &*ATOMIC_TUPLE.1;
+ let _ = &ATOMIC_TUPLE.2;
+ let _ = (&&&&ATOMIC_TUPLE).0;
+ let _ = (&&&&ATOMIC_TUPLE).2;
+ let _ = ATOMIC_TUPLE.0;
+ let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+ let _ = ATOMIC_TUPLE.1.into_iter();
+ let _ = ATOMIC_TUPLE.2;
+ let _ = &{ ATOMIC_TUPLE };
+
+ CELL.set(2); //~ ERROR interior mutability
+ assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
+
+ assert_eq!(INTEGER, 8);
+ assert!(STRING.is_empty());
+
+ let a = ATOMIC;
+ a.store(4, Ordering::SeqCst);
+ assert_eq!(a.load(Ordering::SeqCst), 4);
+
+ STATIC_TUPLE.0.store(3, Ordering::SeqCst);
+ assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
+ assert!(STATIC_TUPLE.1.is_empty());
+
+ assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
+
+ let _ = &CELL_REF.0;
+}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr
new file mode 100644
index 000000000..9a908cf30
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr
@@ -0,0 +1,115 @@
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:54:5
+ |
+LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
+ | ^^^^^^
+ |
+ = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:55:16
+ |
+LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
+ | ^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:58:22
+ |
+LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
+ | ^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:59:25
+ |
+LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
+ | ^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:60:27
+ |
+LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
+ | ^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:61:26
+ |
+LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
+ | ^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:72:14
+ |
+LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:73:14
+ |
+LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:74:19
+ |
+LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:75:14
+ |
+LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:76:13
+ |
+LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:82:13
+ |
+LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:87:5
+ |
+LL | CELL.set(2); //~ ERROR interior mutability
+ | ^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/others.rs:88:16
+ |
+LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
+ | ^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs
new file mode 100644
index 000000000..06b5d62e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs
@@ -0,0 +1,202 @@
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const)]
+
+// this file replicates its `declare` counterpart. Please see it for more discussions.
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+trait ConcreteTypes {
+ const ATOMIC: AtomicUsize;
+ const STRING: String;
+
+ fn function() {
+ let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::STRING;
+ }
+}
+
+impl ConcreteTypes for u64 {
+ const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+ const STRING: String = String::new();
+
+ fn function() {
+ // Lint this again since implementers can choose not to borrow it.
+ let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::STRING;
+ }
+}
+
+// a helper trait used below
+trait ConstDefault {
+ const DEFAULT: Self;
+}
+
+trait GenericTypes<T, U> {
+ const TO_REMAIN_GENERIC: T;
+ const TO_BE_CONCRETE: U;
+
+ fn function() {
+ let _ = &Self::TO_REMAIN_GENERIC;
+ }
+}
+
+impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> {
+ const TO_REMAIN_GENERIC: T = T::DEFAULT;
+ const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
+
+ fn function() {
+ let _ = &Self::TO_REMAIN_GENERIC;
+ let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+ }
+}
+
+// a helper type used below
+pub struct Wrapper<T>(T);
+
+trait AssocTypes {
+ type ToBeFrozen;
+ type ToBeUnfrozen;
+ type ToBeGenericParam;
+
+ const TO_BE_FROZEN: Self::ToBeFrozen;
+ const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
+ const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
+ const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
+
+ fn function() {
+ let _ = &Self::TO_BE_FROZEN;
+ let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
+ }
+}
+
+impl<T: ConstDefault> AssocTypes for Vec<T> {
+ type ToBeFrozen = u16;
+ type ToBeUnfrozen = AtomicUsize;
+ type ToBeGenericParam = T;
+
+ const TO_BE_FROZEN: Self::ToBeFrozen = 12;
+ const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
+ const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
+ const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
+
+ fn function() {
+ let _ = &Self::TO_BE_FROZEN;
+ let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
+ let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+ let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM;
+ }
+}
+
+// a helper trait used below
+trait AssocTypesHelper {
+ type NotToBeBounded;
+ type ToBeBounded;
+
+ const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
+}
+
+trait AssocTypesFromGenericParam<T>
+where
+ T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ const NOT_BOUNDED: T::NotToBeBounded;
+ const BOUNDED: T::ToBeBounded;
+
+ fn function() {
+ let _ = &Self::NOT_BOUNDED;
+ let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ }
+}
+
+impl<T> AssocTypesFromGenericParam<T> for Vec<T>
+where
+ T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+ const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
+
+ fn function() {
+ let _ = &Self::NOT_BOUNDED;
+ let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ }
+}
+
+trait SelfType: Sized {
+ const SELF: Self;
+ const WRAPPED_SELF: Option<Self>;
+
+ fn function() {
+ let _ = &Self::SELF;
+ let _ = &Self::WRAPPED_SELF;
+ }
+}
+
+impl SelfType for u64 {
+ const SELF: Self = 16;
+ const WRAPPED_SELF: Option<Self> = Some(20);
+
+ fn function() {
+ let _ = &Self::SELF;
+ let _ = &Self::WRAPPED_SELF;
+ }
+}
+
+impl SelfType for AtomicUsize {
+ const SELF: Self = AtomicUsize::new(17);
+ const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
+
+ fn function() {
+ let _ = &Self::SELF; //~ ERROR interior mutable
+ let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+ }
+}
+
+trait BothOfCellAndGeneric<T> {
+ const DIRECT: Cell<T>;
+ const INDIRECT: Cell<*const T>;
+
+ fn function() {
+ let _ = &Self::DIRECT;
+ let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ }
+}
+
+impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
+ const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
+ const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
+
+ fn function() {
+ let _ = &Self::DIRECT;
+ let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ }
+}
+
+struct Local<T>(T);
+
+impl<T> Local<T>
+where
+ T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ const ATOMIC: AtomicUsize = AtomicUsize::new(18);
+ const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
+
+ const GENERIC_TYPE: T = T::DEFAULT;
+
+ const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+ const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
+
+ fn function() {
+ let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ let _ = &Self::COW;
+ let _ = &Self::GENERIC_TYPE;
+ let _ = &Self::ASSOC_TYPE;
+ let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+ }
+}
+
+fn main() {
+ u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
+ assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
+}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr
new file mode 100644
index 000000000..8f26403ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr
@@ -0,0 +1,123 @@
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:15:18
+ |
+LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:26:18
+ |
+LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:51:18
+ |
+LL | let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:86:18
+ |
+LL | let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:87:18
+ |
+LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:109:18
+ |
+LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:122:18
+ |
+LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:151:18
+ |
+LL | let _ = &Self::SELF; //~ ERROR interior mutable
+ | ^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:152:18
+ |
+LL | let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:162:18
+ |
+LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:172:18
+ |
+LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:191:18
+ |
+LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:195:18
+ |
+LL | let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:200:5
+ |
+LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
+ | ^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+ --> $DIR/traits.rs:201:16
+ |
+LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
+ | ^^^^^^^^^^^
+ |
+ = help: assign this const to a local or static variable, and use the variable here
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/box_collection.rs b/src/tools/clippy/tests/ui/box_collection.rs
new file mode 100644
index 000000000..1a74cdb3f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/box_collection.rs
@@ -0,0 +1,56 @@
+#![warn(clippy::all)]
+#![allow(
+ clippy::boxed_local,
+ clippy::needless_pass_by_value,
+ clippy::blacklisted_name,
+ unused
+)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+
+macro_rules! boxit {
+ ($init:expr, $x:ty) => {
+ let _: Box<$x> = Box::new($init);
+ };
+}
+
+fn test_macro() {
+ boxit!(Vec::new(), Vec<u8>);
+}
+
+fn test1(foo: Box<Vec<bool>>) {}
+
+fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
+ // pass if #31 is fixed
+ foo(vec![1, 2, 3])
+}
+
+fn test3(foo: Box<String>) {}
+
+fn test4(foo: Box<HashMap<String, String>>) {}
+
+fn test5(foo: Box<HashSet<i64>>) {}
+
+fn test6(foo: Box<VecDeque<i32>>) {}
+
+fn test7(foo: Box<LinkedList<i16>>) {}
+
+fn test8(foo: Box<BTreeMap<i8, String>>) {}
+
+fn test9(foo: Box<BTreeSet<u64>>) {}
+
+fn test10(foo: Box<BinaryHeap<u32>>) {}
+
+fn test_local_not_linted() {
+ let _: Box<Vec<bool>>;
+}
+
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(foo: Box<Vec<bool>>) {}
+
+pub fn pub_test_ret() -> Box<Vec<bool>> {
+ Box::new(Vec::new())
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/box_collection.stderr b/src/tools/clippy/tests/ui/box_collection.stderr
new file mode 100644
index 000000000..2b28598de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/box_collection.stderr
@@ -0,0 +1,75 @@
+error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
+ --> $DIR/box_collection.rs:21:15
+ |
+LL | fn test1(foo: Box<Vec<bool>>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::box-collection` implied by `-D warnings`
+ = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<String>`. Consider using just `String`
+ --> $DIR/box_collection.rs:28:15
+ |
+LL | fn test3(foo: Box<String>) {}
+ | ^^^^^^^^^^^
+ |
+ = help: `String` is already on the heap, `Box<String>` makes an extra allocation
+
+error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
+ --> $DIR/box_collection.rs:30:15
+ |
+LL | fn test4(foo: Box<HashMap<String, String>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<HashSet<..>>`. Consider using just `HashSet<..>`
+ --> $DIR/box_collection.rs:32:15
+ |
+LL | fn test5(foo: Box<HashSet<i64>>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: `HashSet<..>` is already on the heap, `Box<HashSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<VecDeque<..>>`. Consider using just `VecDeque<..>`
+ --> $DIR/box_collection.rs:34:15
+ |
+LL | fn test6(foo: Box<VecDeque<i32>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: `VecDeque<..>` is already on the heap, `Box<VecDeque<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<LinkedList<..>>`. Consider using just `LinkedList<..>`
+ --> $DIR/box_collection.rs:36:15
+ |
+LL | fn test7(foo: Box<LinkedList<i16>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `LinkedList<..>` is already on the heap, `Box<LinkedList<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeMap<..>>`. Consider using just `BTreeMap<..>`
+ --> $DIR/box_collection.rs:38:15
+ |
+LL | fn test8(foo: Box<BTreeMap<i8, String>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BTreeMap<..>` is already on the heap, `Box<BTreeMap<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeSet<..>>`. Consider using just `BTreeSet<..>`
+ --> $DIR/box_collection.rs:40:15
+ |
+LL | fn test9(foo: Box<BTreeSet<u64>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BTreeSet<..>` is already on the heap, `Box<BTreeSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BinaryHeap<..>>`. Consider using just `BinaryHeap<..>`
+ --> $DIR/box_collection.rs:42:16
+ |
+LL | fn test10(foo: Box<BinaryHeap<u32>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BinaryHeap<..>` is already on the heap, `Box<BinaryHeap<..>>` makes an extra allocation
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs
new file mode 100644
index 000000000..4639f00a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/boxed_local.rs
@@ -0,0 +1,209 @@
+#![feature(box_syntax)]
+#![feature(lint_reasons)]
+#![allow(
+ clippy::borrowed_box,
+ clippy::needless_pass_by_value,
+ clippy::unused_unit,
+ clippy::redundant_clone,
+ clippy::match_single_binding
+)]
+#![warn(clippy::boxed_local)]
+
+#[derive(Clone)]
+struct A;
+
+impl A {
+ fn foo(&self) {}
+}
+
+trait Z {
+ fn bar(&self);
+}
+
+impl Z for A {
+ fn bar(&self) {
+ //nothing
+ }
+}
+
+fn main() {}
+
+fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
+ let boxed_local = boxed_trait;
+ // done
+}
+
+fn warn_call() {
+ let x = box A;
+ x.foo();
+}
+
+fn warn_arg(x: Box<A>) {
+ x.foo();
+}
+
+fn nowarn_closure_arg() {
+ let x = Some(box A);
+ x.map_or((), |x| take_ref(&x));
+}
+
+fn warn_rename_call() {
+ let x = box A;
+
+ let y = x;
+ y.foo(); // via autoderef
+}
+
+fn warn_notuse() {
+ let bz = box A;
+}
+
+fn warn_pass() {
+ let bz = box A;
+ take_ref(&bz); // via deref coercion
+}
+
+fn nowarn_return() -> Box<A> {
+ box A // moved out, "escapes"
+}
+
+fn nowarn_move() {
+ let bx = box A;
+ drop(bx) // moved in, "escapes"
+}
+fn nowarn_call() {
+ let bx = box A;
+ bx.clone(); // method only available to Box, not via autoderef
+}
+
+fn nowarn_pass() {
+ let bx = box A;
+ take_box(&bx); // fn needs &Box
+}
+
+fn take_box(x: &Box<A>) {}
+fn take_ref(x: &A) {}
+
+fn nowarn_ref_take() {
+ // false positive, should actually warn
+ let x = box A;
+ let y = &x;
+ take_box(y);
+}
+
+fn nowarn_match() {
+ let x = box A; // moved into a match
+ match x {
+ y => drop(y),
+ }
+}
+
+fn warn_match() {
+ let x = box A;
+ match &x {
+ // not moved
+ y => (),
+ }
+}
+
+fn nowarn_large_array() {
+ // should not warn, is large array
+ // and should not be on stack
+ let x = box [1; 10000];
+ match &x {
+ // not moved
+ y => (),
+ }
+}
+
+/// ICE regression test
+pub trait Foo {
+ type Item;
+}
+
+impl<'a> Foo for &'a () {
+ type Item = ();
+}
+
+pub struct PeekableSeekable<I: Foo> {
+ _peeked: I::Item,
+}
+
+pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
+
+/// Regression for #916, #1123
+///
+/// This shouldn't warn for `boxed_local`as the implementation of a trait
+/// can't change much about the trait definition.
+trait BoxedAction {
+ fn do_sth(self: Box<Self>);
+}
+
+impl BoxedAction for u64 {
+ fn do_sth(self: Box<Self>) {
+ println!("{}", *self)
+ }
+}
+
+/// Regression for #1478
+///
+/// This shouldn't warn for `boxed_local`as self itself is a box type.
+trait MyTrait {
+ fn do_sth(self);
+}
+
+impl<T> MyTrait for Box<T> {
+ fn do_sth(self) {}
+}
+
+// Issue #3739 - capture in closures
+mod issue_3739 {
+ use super::A;
+
+ fn consume<T>(_: T) {}
+ fn borrow<T>(_: &T) {}
+
+ fn closure_consume(x: Box<A>) {
+ let _ = move || {
+ consume(x);
+ };
+ }
+
+ fn closure_borrow(x: Box<A>) {
+ let _ = || {
+ borrow(&x);
+ };
+ }
+}
+
+/// Issue #5542
+///
+/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code.
+pub extern "C" fn do_not_warn_me(_c_pointer: Box<String>) -> () {}
+
+#[rustfmt::skip] // Forces rustfmt to not add ABI
+pub extern fn do_not_warn_me_no_abi(_c_pointer: Box<String>) -> () {}
+
+// Issue #4804 - default implementation in trait
+mod issue4804 {
+ trait DefaultTraitImplTest {
+ // don't warn on `self`
+ fn default_impl(self: Box<Self>) -> u32 {
+ 5
+ }
+
+ // warn on `x: Box<u32>`
+ fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
+ 4
+ }
+ }
+
+ trait WarnTrait {
+ // warn on `x: Box<u32>`
+ fn foo(x: Box<u32>) {}
+ }
+}
+
+fn check_expect(#[expect(clippy::boxed_local)] x: Box<A>) {
+ x.foo();
+}
diff --git a/src/tools/clippy/tests/ui/boxed_local.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr
new file mode 100644
index 000000000..9036529f3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/boxed_local.stderr
@@ -0,0 +1,28 @@
+error: local variable doesn't need to be boxed here
+ --> $DIR/boxed_local.rs:41:13
+ |
+LL | fn warn_arg(x: Box<A>) {
+ | ^
+ |
+ = note: `-D clippy::boxed-local` implied by `-D warnings`
+
+error: local variable doesn't need to be boxed here
+ --> $DIR/boxed_local.rs:132:12
+ |
+LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
+ | ^^^^^^^^^^^
+
+error: local variable doesn't need to be boxed here
+ --> $DIR/boxed_local.rs:196:44
+ |
+LL | fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
+ | ^
+
+error: local variable doesn't need to be boxed here
+ --> $DIR/boxed_local.rs:203:16
+ |
+LL | fn foo(x: Box<u32>) {}
+ | ^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs b/src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs
new file mode 100644
index 000000000..5e3a1a296
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs
@@ -0,0 +1,95 @@
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+use std::sync::Mutex;
+
+// ##################################
+// # Issue clippy#7369
+// ##################################
+#[derive(Debug)]
+pub struct FooBar {
+ foo: Vec<u32>,
+}
+
+impl FooBar {
+ pub fn bar(&mut self) {
+ if true {
+ self.foo.pop();
+ } else {
+ self.baz();
+
+ self.foo.pop();
+
+ self.baz()
+ }
+ }
+
+ fn baz(&mut self) {}
+}
+
+fn foo(x: u32, y: u32) -> u32 {
+ x / y
+}
+
+fn main() {
+ let x = (1, 2);
+ let _ = if true {
+ let (x, y) = x;
+ foo(x, y)
+ } else {
+ let (y, x) = x;
+ foo(x, y)
+ };
+
+ let m = Mutex::new(0u32);
+ let l = m.lock().unwrap();
+ let _ = if true {
+ drop(l);
+ println!("foo");
+ m.lock().unwrap();
+ 0
+ } else if *l == 0 {
+ drop(l);
+ println!("foo");
+ println!("bar");
+ m.lock().unwrap();
+ 1
+ } else {
+ drop(l);
+ println!("foo");
+ println!("baz");
+ m.lock().unwrap();
+ 2
+ };
+
+ if true {
+ let _guard = m.lock();
+ println!("foo");
+ } else {
+ println!("foo");
+ }
+
+ if true {
+ let _guard = m.lock();
+ println!("foo");
+ println!("bar");
+ } else {
+ let _guard = m.lock();
+ println!("foo");
+ println!("baz");
+ }
+
+ let mut c = 0;
+ for _ in 0..5 {
+ if c == 0 {
+ c += 1;
+ println!("0");
+ } else if c == 1 {
+ c += 1;
+ println!("1");
+ } else {
+ c += 1;
+ println!("more");
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
new file mode 100644
index 000000000..12f550d9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
@@ -0,0 +1,223 @@
+#![allow(dead_code, clippy::equatable_if_let)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// This tests the branches_sharing_code lint at the end of blocks
+
+fn simple_examples() {
+ let x = 1;
+
+ let _ = if x == 7 {
+ println!("Branch I");
+ let start_value = 0;
+ println!("=^.^=");
+
+ // Same but not moveable due to `start_value`
+ let _ = start_value;
+
+ // The rest is self contained and moveable => Only lint the rest
+ let result = false;
+ println!("Block end!");
+ result
+ } else {
+ println!("Branch II");
+ let start_value = 8;
+ println!("xD");
+
+ // Same but not moveable due to `start_value`
+ let _ = start_value;
+
+ // The rest is self contained and moveable => Only lint the rest
+ let result = false;
+ println!("Block end!");
+ result
+ };
+
+ // Else if block
+ if x == 9 {
+ println!("The index is: 6");
+
+ println!("Same end of block");
+ } else if x == 8 {
+ println!("The index is: 4");
+
+ // We should only get a lint trigger for the last statement
+ println!("This is also eq with the else block");
+ println!("Same end of block");
+ } else {
+ println!("This is also eq with the else block");
+ println!("Same end of block");
+ }
+
+ // Use of outer scope value
+ let outer_scope_value = "I'm outside the if block";
+ if x < 99 {
+ let z = "How are you";
+ println!("I'm a local because I use the value `z`: `{}`", z);
+
+ println!(
+ "I'm moveable because I know: `outer_scope_value`: '{}'",
+ outer_scope_value
+ );
+ } else {
+ let z = 45678000;
+ println!("I'm a local because I use the value `z`: `{}`", z);
+
+ println!(
+ "I'm moveable because I know: `outer_scope_value`: '{}'",
+ outer_scope_value
+ );
+ }
+
+ if x == 9 {
+ if x == 8 {
+ // No parent!!
+ println!("---");
+ println!("Hello World");
+ } else {
+ println!("Hello World");
+ }
+ }
+}
+
+/// Simple examples where the move can cause some problems due to moved values
+fn simple_but_suggestion_is_invalid() {
+ let x = 16;
+
+ // Local value
+ let later_used_value = 17;
+ if x == 9 {
+ let _ = 9;
+ let later_used_value = "A string value";
+ println!("{}", later_used_value);
+ } else {
+ let later_used_value = "A string value";
+ println!("{}", later_used_value);
+ // I'm expecting a note about this
+ }
+ println!("{}", later_used_value);
+
+ // outer function
+ if x == 78 {
+ let simple_examples = "I now identify as a &str :)";
+ println!("This is the new simple_example: {}", simple_examples);
+ } else {
+ println!("Separator print statement");
+
+ let simple_examples = "I now identify as a &str :)";
+ println!("This is the new simple_example: {}", simple_examples);
+ }
+ simple_examples();
+}
+
+/// Tests where the blocks are not linted due to the used value scope
+fn not_moveable_due_to_value_scope() {
+ let x = 18;
+
+ // Using a local value in the moved code
+ if x == 9 {
+ let y = 18;
+ println!("y is: `{}`", y);
+ } else {
+ let y = "A string";
+ println!("y is: `{}`", y);
+ }
+
+ // Using a local value in the expression
+ let _ = if x == 0 {
+ let mut result = x + 1;
+
+ println!("1. Doing some calculations");
+ println!("2. Some more calculations");
+ println!("3. Setting result");
+
+ result
+ } else {
+ let mut result = x - 1;
+
+ println!("1. Doing some calculations");
+ println!("2. Some more calculations");
+ println!("3. Setting result");
+
+ result
+ };
+
+ let _ = if x == 7 {
+ let z1 = 100;
+ println!("z1: {}", z1);
+
+ let z2 = z1;
+ println!("z2: {}", z2);
+
+ z2
+ } else {
+ let z1 = 300;
+ println!("z1: {}", z1);
+
+ let z2 = z1;
+ println!("z2: {}", z2);
+
+ z2
+ };
+}
+
+/// This should add a note to the lint msg since the moved expression is not `()`
+fn added_note_for_expression_use() -> u32 {
+ let x = 9;
+
+ let _ = if x == 7 {
+ x << 2
+ } else {
+ let _ = 6;
+ x << 2
+ };
+
+ if x == 9 {
+ x * 4
+ } else {
+ let _ = 17;
+ x * 4
+ }
+}
+
+#[rustfmt::skip]
+fn test_suggestion_with_weird_formatting() {
+ let x = 9;
+ let mut a = 0;
+ let mut b = 0;
+
+ // The error message still looks weird tbh but this is the best I can do
+ // for weird formatting
+ if x == 17 { b = 1; a = 0x99; } else { a = 0x99; }
+}
+
+fn fp_test() {
+ let x = 17;
+
+ if x == 18 {
+ let y = 19;
+ if y < x {
+ println!("Trigger")
+ }
+ } else {
+ let z = 166;
+ if z < x {
+ println!("Trigger")
+ }
+ }
+}
+
+fn fp_if_let_issue7054() {
+ // This shouldn't trigger the lint
+ let string;
+ let _x = if let true = true {
+ ""
+ } else if true {
+ string = "x".to_owned();
+ &string
+ } else {
+ string = "y".to_owned();
+ &string
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
new file mode 100644
index 000000000..5e1a68d21
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr
@@ -0,0 +1,143 @@
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:30:5
+ |
+LL | / let result = false;
+LL | | println!("Block end!");
+LL | | result
+LL | | };
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/shared_at_bottom.rs:2:36
+ |
+LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let result = false;
+LL + println!("Block end!");
+LL ~ result;
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:48:5
+ |
+LL | / println!("Same end of block");
+LL | | }
+ | |_____^
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + println!("Same end of block");
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:65:5
+ |
+LL | / println!(
+LL | | "I'm moveable because I know: `outer_scope_value`: '{}'",
+LL | | outer_scope_value
+LL | | );
+LL | | }
+ | |_____^
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + println!(
+LL + "I'm moveable because I know: `outer_scope_value`: '{}'",
+LL + outer_scope_value
+LL + );
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:77:9
+ |
+LL | / println!("Hello World");
+LL | | }
+ | |_________^
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + println!("Hello World");
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:93:5
+ |
+LL | / let later_used_value = "A string value";
+LL | | println!("{}", later_used_value);
+LL | | // I'm expecting a note about this
+LL | | }
+ | |_____^
+ |
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let later_used_value = "A string value";
+LL + println!("{}", later_used_value);
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:106:5
+ |
+LL | / let simple_examples = "I now identify as a &str :)";
+LL | | println!("This is the new simple_example: {}", simple_examples);
+LL | | }
+ | |_____^
+ |
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let simple_examples = "I now identify as a &str :)";
+LL + println!("This is the new simple_example: {}", simple_examples);
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:171:5
+ |
+LL | / x << 2
+LL | | };
+ | |_____^
+ |
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL ~ x << 2;
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:178:5
+ |
+LL | / x * 4
+LL | | }
+ | |_____^
+ |
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + x * 4
+ |
+
+error: all if blocks contain the same code at the end
+ --> $DIR/shared_at_bottom.rs:190:44
+ |
+LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; }
+ | ^^^^^^^^^^^
+ |
+help: consider moving these statements after the if
+ |
+LL ~ if x == 17 { b = 1; a = 0x99; } else { }
+LL + a = 0x99;
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
new file mode 100644
index 000000000..bdeb0a395
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
@@ -0,0 +1,114 @@
+#![allow(dead_code, clippy::mixed_read_write_in_expression)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// This tests the branches_sharing_code lint at the start of blocks
+
+fn simple_examples() {
+ let x = 0;
+
+ // Simple
+ if true {
+ println!("Hello World!");
+ println!("I'm branch nr: 1");
+ } else {
+ println!("Hello World!");
+ println!("I'm branch nr: 2");
+ }
+
+ // Else if
+ if x == 0 {
+ let y = 9;
+ println!("The value y was set to: `{}`", y);
+ let _z = y;
+
+ println!("I'm the true start index of arrays");
+ } else if x == 1 {
+ let y = 9;
+ println!("The value y was set to: `{}`", y);
+ let _z = y;
+
+ println!("I start counting from 1 so my array starts from `1`");
+ } else {
+ let y = 9;
+ println!("The value y was set to: `{}`", y);
+ let _z = y;
+
+ println!("Ha, Pascal allows you to start the array where you want")
+ }
+
+ // Return a value
+ let _ = if x == 7 {
+ let y = 16;
+ println!("What can I say except: \"you're welcome?\"");
+ let _ = y;
+ x
+ } else {
+ let y = 16;
+ println!("Thank you");
+ y
+ };
+}
+
+/// Simple examples where the move can cause some problems due to moved values
+fn simple_but_suggestion_is_invalid() {
+ let x = 10;
+
+ // Can't be automatically moved because used_value_name is getting used again
+ let used_value_name = 19;
+ if x == 10 {
+ let used_value_name = "Different type";
+ println!("Str: {}", used_value_name);
+ let _ = 1;
+ } else {
+ let used_value_name = "Different type";
+ println!("Str: {}", used_value_name);
+ let _ = 2;
+ }
+ let _ = used_value_name;
+
+ // This can be automatically moved as `can_be_overridden` is not used again
+ let can_be_overridden = 8;
+ let _ = can_be_overridden;
+ if x == 11 {
+ let can_be_overridden = "Move me";
+ println!("I'm also moveable");
+ let _ = 111;
+ } else {
+ let can_be_overridden = "Move me";
+ println!("I'm also moveable");
+ let _ = 222;
+ }
+}
+
+/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled.
+fn check_if_same_than_else_mask() {
+ let x = 2021;
+
+ #[allow(clippy::if_same_then_else)]
+ if x == 2020 {
+ println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint.");
+ println!("Because `IF_SAME_THEN_ELSE` is allowed here");
+ } else {
+ println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint.");
+ println!("Because `IF_SAME_THEN_ELSE` is allowed here");
+ }
+
+ if x == 2019 {
+ println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
+ } else {
+ println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
+ }
+}
+
+#[allow(clippy::vec_init_then_push)]
+fn pf_local_with_inferred_type_issue7053() {
+ if true {
+ let mut v = Vec::new();
+ v.push(0);
+ } else {
+ let mut v = Vec::new();
+ v.push("");
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
new file mode 100644
index 000000000..d890b12ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr
@@ -0,0 +1,121 @@
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:10:5
+ |
+LL | / if true {
+LL | | println!("Hello World!");
+ | |_________________________________^
+ |
+note: the lint level is defined here
+ --> $DIR/shared_at_top.rs:2:36
+ |
+LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider moving these statements before the if
+ |
+LL ~ println!("Hello World!");
+LL + if true {
+ |
+
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:19:5
+ |
+LL | / if x == 0 {
+LL | | let y = 9;
+LL | | println!("The value y was set to: `{}`", y);
+LL | | let _z = y;
+ | |___________________^
+ |
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+ |
+LL ~ let y = 9;
+LL + println!("The value y was set to: `{}`", y);
+LL + let _z = y;
+LL + if x == 0 {
+ |
+
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:40:5
+ |
+LL | / let _ = if x == 7 {
+LL | | let y = 16;
+ | |___________________^
+ |
+help: consider moving these statements before the if
+ |
+LL ~ let y = 16;
+LL + let _ = if x == 7 {
+ |
+
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:58:5
+ |
+LL | / if x == 10 {
+LL | | let used_value_name = "Different type";
+LL | | println!("Str: {}", used_value_name);
+ | |_____________________________________________^
+ |
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+ |
+LL ~ let used_value_name = "Different type";
+LL + println!("Str: {}", used_value_name);
+LL + if x == 10 {
+ |
+
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:72:5
+ |
+LL | / if x == 11 {
+LL | | let can_be_overridden = "Move me";
+LL | | println!("I'm also moveable");
+ | |______________________________________^
+ |
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+ |
+LL ~ let can_be_overridden = "Move me";
+LL + println!("I'm also moveable");
+LL + if x == 11 {
+ |
+
+error: all if blocks contain the same code at the start
+ --> $DIR/shared_at_top.rs:88:5
+ |
+LL | / if x == 2020 {
+LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint.");
+LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here");
+ | |________________________________________________________________^
+ |
+help: consider moving these statements before the if
+ |
+LL ~ println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint.");
+LL + println!("Because `IF_SAME_THEN_ELSE` is allowed here");
+LL + if x == 2020 {
+ |
+
+error: this `if` has identical blocks
+ --> $DIR/shared_at_top.rs:96:18
+ |
+LL | if x == 2019 {
+ | __________________^
+LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
+LL | | } else {
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/shared_at_top.rs:2:9
+ |
+LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+note: same as this
+ --> $DIR/shared_at_top.rs:98:12
+ |
+LL | } else {
+ | ____________^
+LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
+LL | | }
+ | |_____^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
new file mode 100644
index 000000000..deefdad32
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs
@@ -0,0 +1,119 @@
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// branches_sharing_code at the top and bottom of the if blocks
+
+struct DataPack {
+ id: u32,
+ name: String,
+ some_data: Vec<u8>,
+}
+
+fn overlapping_eq_regions() {
+ let x = 9;
+
+ // Overlap with separator
+ if x == 7 {
+ let t = 7;
+ let _overlap_start = t * 2;
+ let _overlap_end = 2 * t;
+ let _u = 9;
+ } else {
+ let t = 7;
+ let _overlap_start = t * 2;
+ let _overlap_end = 2 * t;
+ println!("Overlap separator");
+ let _overlap_start = t * 2;
+ let _overlap_end = 2 * t;
+ let _u = 9;
+ }
+
+ // Overlap with separator
+ if x == 99 {
+ let r = 7;
+ let _overlap_start = r;
+ let _overlap_middle = r * r;
+ let _overlap_end = r * r * r;
+ let z = "end";
+ } else {
+ let r = 7;
+ let _overlap_start = r;
+ let _overlap_middle = r * r;
+ let _overlap_middle = r * r;
+ let _overlap_end = r * r * r;
+ let z = "end";
+ }
+}
+
+fn complexer_example() {
+ fn gen_id(x: u32, y: u32) -> u32 {
+ let x = x & 0x0000_ffff;
+ let y = (y & 0xffff_0000) << 16;
+ x | y
+ }
+
+ fn process_data(data: DataPack) {
+ let _ = data;
+ }
+
+ let x = 8;
+ let y = 9;
+ if (x > 7 && y < 13) || (x + y) % 2 == 1 {
+ let a = 0xcafe;
+ let b = 0xffff00ff;
+ let e_id = gen_id(a, b);
+
+ println!("From the a `{}` to the b `{}`", a, b);
+
+ let pack = DataPack {
+ id: e_id,
+ name: "Player 1".to_string(),
+ some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
+ };
+ process_data(pack);
+ } else {
+ let a = 0xcafe;
+ let b = 0xffff00ff;
+ let e_id = gen_id(a, b);
+
+ println!("The new ID is '{}'", e_id);
+
+ let pack = DataPack {
+ id: e_id,
+ name: "Player 1".to_string(),
+ some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
+ };
+ process_data(pack);
+ }
+}
+
+/// This should add a note to the lint msg since the moved expression is not `()`
+fn added_note_for_expression_use() -> u32 {
+ let x = 9;
+
+ let _ = if x == 7 {
+ let _ = 19;
+
+ let _splitter = 6;
+
+ x << 2
+ } else {
+ let _ = 19;
+
+ x << 2
+ };
+
+ if x == 9 {
+ let _ = 17;
+
+ let _splitter = 6;
+
+ x * 4
+ } else {
+ let _ = 17;
+
+ x * 4
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
new file mode 100644
index 000000000..a270f637f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr
@@ -0,0 +1,155 @@
+error: all if blocks contain the same code at both the start and the end
+ --> $DIR/shared_at_top_and_bottom.rs:16:5
+ |
+LL | / if x == 7 {
+LL | | let t = 7;
+LL | | let _overlap_start = t * 2;
+LL | | let _overlap_end = 2 * t;
+ | |_________________________________^
+ |
+note: the lint level is defined here
+ --> $DIR/shared_at_top_and_bottom.rs:2:36
+ |
+LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: this code is shared at the end
+ --> $DIR/shared_at_top_and_bottom.rs:28:5
+ |
+LL | / let _u = 9;
+LL | | }
+ | |_____^
+help: consider moving these statements before the if
+ |
+LL ~ let t = 7;
+LL + let _overlap_start = t * 2;
+LL + let _overlap_end = 2 * t;
+LL + if x == 7 {
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let _u = 9;
+ |
+
+error: all if blocks contain the same code at both the start and the end
+ --> $DIR/shared_at_top_and_bottom.rs:32:5
+ |
+LL | / if x == 99 {
+LL | | let r = 7;
+LL | | let _overlap_start = r;
+LL | | let _overlap_middle = r * r;
+ | |____________________________________^
+ |
+note: this code is shared at the end
+ --> $DIR/shared_at_top_and_bottom.rs:43:5
+ |
+LL | / let _overlap_end = r * r * r;
+LL | | let z = "end";
+LL | | }
+ | |_____^
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+ |
+LL ~ let r = 7;
+LL + let _overlap_start = r;
+LL + let _overlap_middle = r * r;
+LL + if x == 99 {
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let _overlap_end = r * r * r;
+LL + let z = "end";
+ |
+
+error: all if blocks contain the same code at both the start and the end
+ --> $DIR/shared_at_top_and_bottom.rs:61:5
+ |
+LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 {
+LL | | let a = 0xcafe;
+LL | | let b = 0xffff00ff;
+LL | | let e_id = gen_id(a, b);
+ | |________________________________^
+ |
+note: this code is shared at the end
+ --> $DIR/shared_at_top_and_bottom.rs:81:5
+ |
+LL | / let pack = DataPack {
+LL | | id: e_id,
+LL | | name: "Player 1".to_string(),
+LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
+LL | | };
+LL | | process_data(pack);
+LL | | }
+ | |_____^
+ = warning: some moved values might need to be renamed to avoid wrong references
+help: consider moving these statements before the if
+ |
+LL ~ let a = 0xcafe;
+LL + let b = 0xffff00ff;
+LL + let e_id = gen_id(a, b);
+LL + if (x > 7 && y < 13) || (x + y) % 2 == 1 {
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + let pack = DataPack {
+LL + id: e_id,
+LL + name: "Player 1".to_string(),
+LL + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90],
+LL + };
+LL + process_data(pack);
+ |
+
+error: all if blocks contain the same code at both the start and the end
+ --> $DIR/shared_at_top_and_bottom.rs:94:5
+ |
+LL | / let _ = if x == 7 {
+LL | | let _ = 19;
+ | |___________________^
+ |
+note: this code is shared at the end
+ --> $DIR/shared_at_top_and_bottom.rs:103:5
+ |
+LL | / x << 2
+LL | | };
+ | |_____^
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements before the if
+ |
+LL ~ let _ = 19;
+LL + let _ = if x == 7 {
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL ~ x << 2;
+ |
+
+error: all if blocks contain the same code at both the start and the end
+ --> $DIR/shared_at_top_and_bottom.rs:106:5
+ |
+LL | / if x == 9 {
+LL | | let _ = 17;
+ | |___________________^
+ |
+note: this code is shared at the end
+ --> $DIR/shared_at_top_and_bottom.rs:115:5
+ |
+LL | / x * 4
+LL | | }
+ | |_____^
+ = note: the end suggestion probably needs some adjustments to use the expression result correctly
+help: consider moving these statements before the if
+ |
+LL ~ let _ = 17;
+LL + if x == 9 {
+ |
+help: consider moving these statements after the if
+ |
+LL ~ }
+LL + x * 4
+ |
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs
new file mode 100644
index 000000000..a26141be2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs
@@ -0,0 +1,155 @@
+#![allow(dead_code, clippy::mixed_read_write_in_expression)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// This tests valid if blocks that shouldn't trigger the lint
+
+// Tests with value references are includes in "shared_code_at_bottom.rs"
+
+fn valid_examples() {
+ let x = 2;
+
+ // The edge statements are different
+ if x == 9 {
+ let y = 1 << 5;
+
+ println!("This is the same: vvv");
+ let _z = y;
+ println!("The block expression is different");
+
+ println!("Different end 1");
+ } else {
+ let y = 1 << 7;
+
+ println!("This is the same: vvv");
+ let _z = y;
+ println!("The block expression is different");
+
+ println!("Different end 2");
+ }
+
+ // No else
+ if x == 2 {
+ println!("Hello world!");
+ println!("Hello back, how are you?");
+
+ // This is different vvvv
+ println!("Howdy stranger =^.^=");
+
+ println!("Bye Bye World");
+ } else if x == 9 {
+ println!("Hello world!");
+ println!("Hello back, how are you?");
+
+ // This is different vvvv
+ println!("Hello reviewer :D");
+
+ println!("Bye Bye World");
+ }
+
+ // Overlapping statements only in else if blocks -> Don't lint
+ if x == 0 {
+ println!("I'm important!")
+ } else if x == 17 {
+ println!("I share code in else if");
+
+ println!("x is 17");
+ } else {
+ println!("I share code in else if");
+
+ println!("x is nether x nor 17");
+ }
+
+ // Mutability is different
+ if x == 13 {
+ let mut y = 9;
+ println!("Value y is: {}", y);
+ y += 16;
+ let _z1 = y;
+ } else {
+ let y = 9;
+ println!("Value y is: {}", y);
+ let _z2 = y;
+ }
+
+ // Same blocks but at start and bottom so no `if_same_then_else` lint
+ if x == 418 {
+ let y = 9;
+ let z = 8;
+ let _ = (x, y, z);
+ // Don't tell the programmer, my code is also in the else block
+ } else if x == 419 {
+ println!("+-----------+");
+ println!("| |");
+ println!("| O O |");
+ println!("| ° |");
+ println!("| \\_____/ |");
+ println!("| |");
+ println!("+-----------+");
+ } else {
+ let y = 9;
+ let z = 8;
+ let _ = (x, y, z);
+ // I'm so much better than the x == 418 block. Trust me
+ }
+
+ let x = 1;
+ if true {
+ println!("{}", x);
+ } else {
+ let x = 2;
+ println!("{}", x);
+ }
+
+ // Let's test empty blocks
+ if false {
+ } else {
+ }
+}
+
+/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint
+fn trigger_other_lint() {
+ let x = 0;
+ let y = 1;
+
+ // Same block
+ if x == 0 {
+ let u = 19;
+ println!("How are u today?");
+ let _ = "This is a string";
+ } else {
+ let u = 19;
+ println!("How are u today?");
+ let _ = "This is a string";
+ }
+
+ // Only same expression
+ let _ = if x == 6 { 7 } else { 7 };
+
+ // Same in else if block
+ let _ = if x == 67 {
+ println!("Well I'm the most important block");
+ "I'm a pretty string"
+ } else if x == 68 {
+ println!("I'm a doppelgänger");
+ // Don't listen to my clone below
+
+ if y == 90 { "=^.^=" } else { ":D" }
+ } else {
+ // Don't listen to my clone above
+ println!("I'm a doppelgänger");
+
+ if y == 90 { "=^.^=" } else { ":D" }
+ };
+
+ if x == 0 {
+ println!("I'm single");
+ } else if x == 68 {
+ println!("I'm a doppelgänger");
+ // Don't listen to my clone below
+ } else {
+ // Don't listen to my clone above
+ println!("I'm a doppelgänger");
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr
new file mode 100644
index 000000000..a815995e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr
@@ -0,0 +1,101 @@
+error: this `if` has identical blocks
+ --> $DIR/valid_if_blocks.rs:104:14
+ |
+LL | if false {
+ | ______________^
+LL | | } else {
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/valid_if_blocks.rs:2:9
+ |
+LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+note: same as this
+ --> $DIR/valid_if_blocks.rs:105:12
+ |
+LL | } else {
+ | ____________^
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/valid_if_blocks.rs:115:15
+ |
+LL | if x == 0 {
+ | _______________^
+LL | | let u = 19;
+LL | | println!("How are u today?");
+LL | | let _ = "This is a string";
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/valid_if_blocks.rs:119:12
+ |
+LL | } else {
+ | ____________^
+LL | | let u = 19;
+LL | | println!("How are u today?");
+LL | | let _ = "This is a string";
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/valid_if_blocks.rs:126:23
+ |
+LL | let _ = if x == 6 { 7 } else { 7 };
+ | ^^^^^
+ |
+note: same as this
+ --> $DIR/valid_if_blocks.rs:126:34
+ |
+LL | let _ = if x == 6 { 7 } else { 7 };
+ | ^^^^^
+
+error: this `if` has identical blocks
+ --> $DIR/valid_if_blocks.rs:132:23
+ |
+LL | } else if x == 68 {
+ | _______________________^
+LL | | println!("I'm a doppelgänger");
+LL | | // Don't listen to my clone below
+LL | |
+LL | | if y == 90 { "=^.^=" } else { ":D" }
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/valid_if_blocks.rs:137:12
+ |
+LL | } else {
+ | ____________^
+LL | | // Don't listen to my clone above
+LL | | println!("I'm a doppelgänger");
+LL | |
+LL | | if y == 90 { "=^.^=" } else { ":D" }
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/valid_if_blocks.rs:146:23
+ |
+LL | } else if x == 68 {
+ | _______________________^
+LL | | println!("I'm a doppelgänger");
+LL | | // Don't listen to my clone below
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/valid_if_blocks.rs:149:12
+ |
+LL | } else {
+ | ____________^
+LL | | // Don't listen to my clone above
+LL | | println!("I'm a doppelgänger");
+LL | | }
+ | |_____^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.rs b/src/tools/clippy/tests/ui/builtin_type_shadow.rs
new file mode 100644
index 000000000..69b8b6a0e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/builtin_type_shadow.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::builtin_type_shadow)]
+#![allow(non_camel_case_types)]
+
+fn foo<u32>(a: u32) -> u32 {
+ 42
+ // ^ rustc's type error
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
new file mode 100644
index 000000000..47a8a1e62
--- /dev/null
+++ b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
@@ -0,0 +1,24 @@
+error: this generic shadows the built-in type `u32`
+ --> $DIR/builtin_type_shadow.rs:4:8
+ |
+LL | fn foo<u32>(a: u32) -> u32 {
+ | ^^^
+ |
+ = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
+
+error[E0308]: mismatched types
+ --> $DIR/builtin_type_shadow.rs:5:5
+ |
+LL | fn foo<u32>(a: u32) -> u32 {
+ | --- --- expected `u32` because of return type
+ | |
+ | this type parameter
+LL | 42
+ | ^^ expected type parameter `u32`, found integer
+ |
+ = note: expected type parameter `u32`
+ found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/tools/clippy/tests/ui/bytecount.rs b/src/tools/clippy/tests/ui/bytecount.rs
new file mode 100644
index 000000000..d3ad26921
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytecount.rs
@@ -0,0 +1,26 @@
+#![allow(clippy::needless_borrow)]
+
+#[deny(clippy::naive_bytecount)]
+fn main() {
+ let x = vec![0_u8; 16];
+
+ let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
+
+ let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
+
+ let _ = x.iter().filter(|a| **a > 0).count(); // not an equality count, OK.
+
+ let _ = x.iter().map(|a| a + 1).filter(|&a| a < 15).count(); // not a slice
+
+ let b = 0;
+
+ let _ = x.iter().filter(|_| b > 0).count(); // woah there
+
+ let _ = x.iter().filter(|_a| b == b + 1).count(); // nothing to see here, move along
+
+ let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
+
+ let y = vec![0_u16; 3];
+
+ let _ = y.iter().filter(|&&a| a == 0).count(); // naive count, but not bytes
+}
diff --git a/src/tools/clippy/tests/ui/bytecount.stderr b/src/tools/clippy/tests/ui/bytecount.stderr
new file mode 100644
index 000000000..68d838c1f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytecount.stderr
@@ -0,0 +1,26 @@
+error: you appear to be counting bytes the naive way
+ --> $DIR/bytecount.rs:7:13
+ |
+LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
+ |
+note: the lint level is defined here
+ --> $DIR/bytecount.rs:3:8
+ |
+LL | #[deny(clippy::naive_bytecount)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: you appear to be counting bytes the naive way
+ --> $DIR/bytecount.rs:9:13
+ |
+LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
+
+error: you appear to be counting bytes the naive way
+ --> $DIR/bytecount.rs:21:13
+ |
+LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.fixed b/src/tools/clippy/tests/ui/bytes_count_to_len.fixed
new file mode 100644
index 000000000..860642363
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_count_to_len.fixed
@@ -0,0 +1,34 @@
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+ // should fix, because type is String
+ let _ = String::from("foo").len();
+
+ let s1 = String::from("foo");
+ let _ = s1.len();
+
+ // should fix, because type is &str
+ let _ = "foo".len();
+
+ let s2 = "foo";
+ let _ = s2.len();
+
+ // make sure using count() normally doesn't trigger warning
+ let vector = [0, 1, 2];
+ let _ = vector.iter().count();
+
+ // The type is slice, so should not fix
+ let _ = &[1, 2, 3].bytes().count();
+
+ let bytes: &[u8] = &[1, 2, 3];
+ bytes.bytes().count();
+
+ // The type is File, so should not fix
+ let _ = File::open("foobar").unwrap().bytes().count();
+
+ let f = File::open("foobar").unwrap();
+ let _ = f.bytes().count();
+}
diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.rs b/src/tools/clippy/tests/ui/bytes_count_to_len.rs
new file mode 100644
index 000000000..162730c28
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_count_to_len.rs
@@ -0,0 +1,34 @@
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+ // should fix, because type is String
+ let _ = String::from("foo").bytes().count();
+
+ let s1 = String::from("foo");
+ let _ = s1.bytes().count();
+
+ // should fix, because type is &str
+ let _ = "foo".bytes().count();
+
+ let s2 = "foo";
+ let _ = s2.bytes().count();
+
+ // make sure using count() normally doesn't trigger warning
+ let vector = [0, 1, 2];
+ let _ = vector.iter().count();
+
+ // The type is slice, so should not fix
+ let _ = &[1, 2, 3].bytes().count();
+
+ let bytes: &[u8] = &[1, 2, 3];
+ bytes.bytes().count();
+
+ // The type is File, so should not fix
+ let _ = File::open("foobar").unwrap().bytes().count();
+
+ let f = File::open("foobar").unwrap();
+ let _ = f.bytes().count();
+}
diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.stderr b/src/tools/clippy/tests/ui/bytes_count_to_len.stderr
new file mode 100644
index 000000000..224deb779
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_count_to_len.stderr
@@ -0,0 +1,28 @@
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:8:13
+ |
+LL | let _ = String::from("foo").bytes().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()`
+ |
+ = note: `-D clippy::bytes-count-to-len` implied by `-D warnings`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:11:13
+ |
+LL | let _ = s1.bytes().count();
+ | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:14:13
+ |
+LL | let _ = "foo".bytes().count();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:17:13
+ |
+LL | let _ = s2.bytes().count();
+ | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bytes_nth.fixed b/src/tools/clippy/tests/ui/bytes_nth.fixed
new file mode 100644
index 000000000..b1fb2e16b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_nth.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![allow(clippy::unnecessary_operation)]
+#![warn(clippy::bytes_nth)]
+
+fn main() {
+ let s = String::from("String");
+ let _ = s.as_bytes().get(3);
+ let _ = &s.as_bytes().get(3);
+ let _ = s[..].as_bytes().get(3);
+}
diff --git a/src/tools/clippy/tests/ui/bytes_nth.rs b/src/tools/clippy/tests/ui/bytes_nth.rs
new file mode 100644
index 000000000..034c54e6a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_nth.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![allow(clippy::unnecessary_operation)]
+#![warn(clippy::bytes_nth)]
+
+fn main() {
+ let s = String::from("String");
+ let _ = s.bytes().nth(3);
+ let _ = &s.bytes().nth(3);
+ let _ = s[..].bytes().nth(3);
+}
diff --git a/src/tools/clippy/tests/ui/bytes_nth.stderr b/src/tools/clippy/tests/ui/bytes_nth.stderr
new file mode 100644
index 000000000..9851d4791
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bytes_nth.stderr
@@ -0,0 +1,22 @@
+error: called `.bytes().nth()` on a `String`
+ --> $DIR/bytes_nth.rs:8:13
+ |
+LL | let _ = s.bytes().nth(3);
+ | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
+ |
+ = note: `-D clippy::bytes-nth` implied by `-D warnings`
+
+error: called `.bytes().nth()` on a `String`
+ --> $DIR/bytes_nth.rs:9:14
+ |
+LL | let _ = &s.bytes().nth(3);
+ | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
+
+error: called `.bytes().nth()` on a `str`
+ --> $DIR/bytes_nth.rs:10:13
+ |
+LL | let _ = s[..].bytes().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
new file mode 100644
index 000000000..0d65071af
--- /dev/null
+++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
@@ -0,0 +1,44 @@
+#![warn(clippy::case_sensitive_file_extension_comparisons)]
+
+use std::string::String;
+
+struct TestStruct;
+
+impl TestStruct {
+ fn ends_with(self, arg: &str) {}
+}
+
+fn is_rust_file(filename: &str) -> bool {
+ filename.ends_with(".rs")
+}
+
+fn main() {
+ // std::string::String and &str should trigger the lint failure with .ext12
+ let _ = String::from("").ends_with(".ext12");
+ let _ = "str".ends_with(".ext12");
+
+ // The test struct should not trigger the lint failure with .ext12
+ TestStruct {}.ends_with(".ext12");
+
+ // std::string::String and &str should trigger the lint failure with .EXT12
+ let _ = String::from("").ends_with(".EXT12");
+ let _ = "str".ends_with(".EXT12");
+
+ // The test struct should not trigger the lint failure with .EXT12
+ TestStruct {}.ends_with(".EXT12");
+
+ // Should not trigger the lint failure with .eXT12
+ let _ = String::from("").ends_with(".eXT12");
+ let _ = "str".ends_with(".eXT12");
+ TestStruct {}.ends_with(".eXT12");
+
+ // Should not trigger the lint failure with .EXT123 (too long)
+ let _ = String::from("").ends_with(".EXT123");
+ let _ = "str".ends_with(".EXT123");
+ TestStruct {}.ends_with(".EXT123");
+
+ // Shouldn't fail if it doesn't start with a dot
+ let _ = String::from("").ends_with("a.ext");
+ let _ = "str".ends_with("a.extA");
+ TestStruct {}.ends_with("a.ext");
+}
diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
new file mode 100644
index 000000000..05b98169f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
@@ -0,0 +1,43 @@
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14
+ |
+LL | filename.ends_with(".rs")
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings`
+ = help: consider using a case-insensitive comparison instead
+
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
+ |
+LL | let _ = String::from("").ends_with(".ext12");
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a case-insensitive comparison instead
+
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19
+ |
+LL | let _ = "str".ends_with(".ext12");
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a case-insensitive comparison instead
+
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
+ |
+LL | let _ = String::from("").ends_with(".EXT12");
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a case-insensitive comparison instead
+
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19
+ |
+LL | let _ = "str".ends_with(".EXT12");
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a case-insensitive comparison instead
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
new file mode 100644
index 000000000..e6031e9ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -0,0 +1,262 @@
+#![feature(repr128)]
+#![allow(incomplete_features)]
+#![warn(
+ clippy::cast_precision_loss,
+ clippy::cast_possible_truncation,
+ clippy::cast_sign_loss,
+ clippy::cast_possible_wrap
+)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
+fn main() {
+ // Test clippy::cast_precision_loss
+ let x0 = 1i32;
+ x0 as f32;
+ let x1 = 1i64;
+ x1 as f32;
+ x1 as f64;
+ let x2 = 1u32;
+ x2 as f32;
+ let x3 = 1u64;
+ x3 as f32;
+ x3 as f64;
+ // Test clippy::cast_possible_truncation
+ 1f32 as i32;
+ 1f32 as u32;
+ 1f64 as f32;
+ 1i32 as i8;
+ 1i32 as u8;
+ 1f64 as isize;
+ 1f64 as usize;
+ // Test clippy::cast_possible_wrap
+ 1u8 as i8;
+ 1u16 as i16;
+ 1u32 as i32;
+ 1u64 as i64;
+ 1usize as isize;
+ // Test clippy::cast_sign_loss
+ 1i32 as u32;
+ -1i32 as u32;
+ 1isize as usize;
+ -1isize as usize;
+ 0i8 as u8;
+ i8::MAX as u8;
+ i16::MAX as u16;
+ i32::MAX as u32;
+ i64::MAX as u64;
+ i128::MAX as u128;
+
+ (-1i8).abs() as u8;
+ (-1i16).abs() as u16;
+ (-1i32).abs() as u32;
+ (-1i64).abs() as u64;
+ (-1isize).abs() as usize;
+
+ (-1i8).checked_abs().unwrap() as u8;
+ (-1i16).checked_abs().unwrap() as u16;
+ (-1i32).checked_abs().unwrap() as u32;
+ (-1i64).checked_abs().unwrap() as u64;
+ (-1isize).checked_abs().unwrap() as usize;
+
+ (-1i8).rem_euclid(1i8) as u8;
+ (-1i8).rem_euclid(1i8) as u16;
+ (-1i16).rem_euclid(1i16) as u16;
+ (-1i16).rem_euclid(1i16) as u32;
+ (-1i32).rem_euclid(1i32) as u32;
+ (-1i32).rem_euclid(1i32) as u64;
+ (-1i64).rem_euclid(1i64) as u64;
+ (-1i64).rem_euclid(1i64) as u128;
+ (-1isize).rem_euclid(1isize) as usize;
+ (1i8).rem_euclid(-1i8) as u8;
+ (1i8).rem_euclid(-1i8) as u16;
+ (1i16).rem_euclid(-1i16) as u16;
+ (1i16).rem_euclid(-1i16) as u32;
+ (1i32).rem_euclid(-1i32) as u32;
+ (1i32).rem_euclid(-1i32) as u64;
+ (1i64).rem_euclid(-1i64) as u64;
+ (1i64).rem_euclid(-1i64) as u128;
+ (1isize).rem_euclid(-1isize) as usize;
+
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
+ (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
+ (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
+
+ // no lint for `cast_possible_truncation`
+ // with `signum` method call (see issue #5395)
+ let x: i64 = 5;
+ let _ = x.signum() as i32;
+
+ let s = x.signum();
+ let _ = s as i32;
+
+ // Test for signed min
+ (-99999999999i64).min(1) as i8; // should be linted because signed
+
+ // Test for various operations that remove enough bits for the result to fit
+ (999999u64 & 1) as u8;
+ (999999u64 % 15) as u8;
+ (999999u64 / 0x1_0000_0000_0000) as u16;
+ ({ 999999u64 >> 56 }) as u8;
+ ({
+ let x = 999999u64;
+ x.min(1)
+ }) as u8;
+ 999999u64.clamp(0, 255) as u8;
+ 999999u64.clamp(0, 256) as u8; // should still be linted
+
+ #[derive(Clone, Copy)]
+ enum E1 {
+ A,
+ B,
+ C,
+ }
+ impl E1 {
+ fn test(self) {
+ let _ = self as u8; // Don't lint. `0..=2` fits in u8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E2 {
+ A = 255,
+ B,
+ }
+ impl E2 {
+ fn test(self) {
+ let _ = self as u8;
+ let _ = Self::B as u8;
+ let _ = self as i16; // Don't lint. `255..=256` fits in i16
+ let _ = Self::A as u8; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E3 {
+ A = -1,
+ B,
+ C = 50,
+ }
+ impl E3 {
+ fn test(self) {
+ let _ = self as i8; // Don't lint. `-1..=50` fits in i8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E4 {
+ A = -128,
+ B,
+ }
+ impl E4 {
+ fn test(self) {
+ let _ = self as i8; // Don't lint. `-128..=-127` fits in i8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E5 {
+ A = -129,
+ B = 127,
+ }
+ impl E5 {
+ fn test(self) {
+ let _ = self as i8;
+ let _ = Self::A as i8;
+ let _ = self as i16; // Don't lint. `-129..=127` fits in i16
+ let _ = Self::B as u8; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u32)]
+ enum E6 {
+ A = u16::MAX as u32,
+ B,
+ }
+ impl E6 {
+ fn test(self) {
+ let _ = self as i16;
+ let _ = Self::A as u16; // Don't lint. `2^16-1` fits in u16
+ let _ = self as u32; // Don't lint. `2^16-1..=2^16` fits in u32
+ let _ = Self::A as u16; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u64)]
+ enum E7 {
+ A = u32::MAX as u64,
+ B,
+ }
+ impl E7 {
+ fn test(self) {
+ let _ = self as usize;
+ let _ = Self::A as usize; // Don't lint.
+ let _ = self as u64; // Don't lint. `2^32-1..=2^32` fits in u64
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(i128)]
+ enum E8 {
+ A = i128::MIN,
+ B,
+ C = 0,
+ D = i128::MAX,
+ }
+ impl E8 {
+ fn test(self) {
+ let _ = self as i128; // Don't lint. `-(2^127)..=2^127-1` fits it i128
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u128)]
+ enum E9 {
+ A,
+ B = u128::MAX,
+ }
+ impl E9 {
+ fn test(self) {
+ let _ = Self::A as u8; // Don't lint.
+ let _ = self as u128; // Don't lint. `0..=2^128-1` fits in u128
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(usize)]
+ enum E10 {
+ A,
+ B = u32::MAX as usize,
+ }
+ impl E10 {
+ fn test(self) {
+ let _ = self as u16;
+ let _ = Self::B as u32; // Don't lint.
+ let _ = self as u64; // Don't lint.
+ }
+ }
+}
+
+fn avoid_subtract_overflow(q: u32) {
+ let c = (q >> 16) as u8;
+ c as usize;
+
+ let c = (q / 1000) as u8;
+ c as usize;
+}
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
new file mode 100644
index 000000000..0c63b4af3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -0,0 +1,210 @@
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:14:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:16:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:17:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:19:5
+ |
+LL | x2 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:21:5
+ |
+LL | x3 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:22:5
+ |
+LL | x3 as f64;
+ | ^^^^^^^^^
+
+error: casting `f32` to `i32` may truncate the value
+ --> $DIR/cast.rs:24:5
+ |
+LL | 1f32 as i32;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+
+error: casting `f32` to `u32` may truncate the value
+ --> $DIR/cast.rs:25:5
+ |
+LL | 1f32 as u32;
+ | ^^^^^^^^^^^
+
+error: casting `f32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:25:5
+ |
+LL | 1f32 as u32;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
+
+error: casting `f64` to `f32` may truncate the value
+ --> $DIR/cast.rs:26:5
+ |
+LL | 1f64 as f32;
+ | ^^^^^^^^^^^
+
+error: casting `i32` to `i8` may truncate the value
+ --> $DIR/cast.rs:27:5
+ |
+LL | 1i32 as i8;
+ | ^^^^^^^^^^
+
+error: casting `i32` to `u8` may truncate the value
+ --> $DIR/cast.rs:28:5
+ |
+LL | 1i32 as u8;
+ | ^^^^^^^^^^
+
+error: casting `f64` to `isize` may truncate the value
+ --> $DIR/cast.rs:29:5
+ |
+LL | 1f64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `f64` to `usize` may truncate the value
+ --> $DIR/cast.rs:30:5
+ |
+LL | 1f64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `f64` to `usize` may lose the sign of the value
+ --> $DIR/cast.rs:30:5
+ |
+LL | 1f64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u8` to `i8` may wrap around the value
+ --> $DIR/cast.rs:32:5
+ |
+LL | 1u8 as i8;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+
+error: casting `u16` to `i16` may wrap around the value
+ --> $DIR/cast.rs:33:5
+ |
+LL | 1u16 as i16;
+ | ^^^^^^^^^^^
+
+error: casting `u32` to `i32` may wrap around the value
+ --> $DIR/cast.rs:34:5
+ |
+LL | 1u32 as i32;
+ | ^^^^^^^^^^^
+
+error: casting `u64` to `i64` may wrap around the value
+ --> $DIR/cast.rs:35:5
+ |
+LL | 1u64 as i64;
+ | ^^^^^^^^^^^
+
+error: casting `usize` to `isize` may wrap around the value
+ --> $DIR/cast.rs:36:5
+ |
+LL | 1usize as isize;
+ | ^^^^^^^^^^^^^^^
+
+error: casting `i32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:39:5
+ |
+LL | -1i32 as u32;
+ | ^^^^^^^^^^^^
+
+error: casting `isize` to `usize` may lose the sign of the value
+ --> $DIR/cast.rs:41:5
+ |
+LL | -1isize as usize;
+ | ^^^^^^^^^^^^^^^^
+
+error: casting `i64` to `i8` may truncate the value
+ --> $DIR/cast.rs:108:5
+ |
+LL | (-99999999999i64).min(1) as i8; // should be linted because signed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: casting `u64` to `u8` may truncate the value
+ --> $DIR/cast.rs:120:5
+ |
+LL | 999999u64.clamp(0, 256) as u8; // should still be linted
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: casting `main::E2` to `u8` may truncate the value
+ --> $DIR/cast.rs:141:21
+ |
+LL | let _ = self as u8;
+ | ^^^^^^^^^^
+
+error: casting `main::E2::B` to `u8` will truncate the value
+ --> $DIR/cast.rs:142:21
+ |
+LL | let _ = Self::B as u8;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
+
+error: casting `main::E5` to `i8` may truncate the value
+ --> $DIR/cast.rs:178:21
+ |
+LL | let _ = self as i8;
+ | ^^^^^^^^^^
+
+error: casting `main::E5::A` to `i8` will truncate the value
+ --> $DIR/cast.rs:179:21
+ |
+LL | let _ = Self::A as i8;
+ | ^^^^^^^^^^^^^
+
+error: casting `main::E6` to `i16` may truncate the value
+ --> $DIR/cast.rs:193:21
+ |
+LL | let _ = self as i16;
+ | ^^^^^^^^^^^
+
+error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast.rs:208:21
+ |
+LL | let _ = self as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `main::E10` to `u16` may truncate the value
+ --> $DIR/cast.rs:249:21
+ |
+LL | let _ = self as u16;
+ | ^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:257:13
+ |
+LL | let c = (q >> 16) as u8;
+ | ^^^^^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:260:13
+ |
+LL | let c = (q / 1000) as u8;
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
new file mode 100644
index 000000000..a68b32b09
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+ let x: i32 = -42;
+ let y: u32 = x.unsigned_abs();
+ println!("The absolute value of {} is {}", x, y);
+
+ let a: i32 = -3;
+ let _: usize = a.unsigned_abs() as usize;
+ let _: usize = a.unsigned_abs() as _;
+ let _ = a.unsigned_abs() as usize;
+
+ let a: i64 = -3;
+ let _ = a.unsigned_abs() as usize;
+ let _ = a.unsigned_abs() as u8;
+ let _ = a.unsigned_abs() as u16;
+ let _ = a.unsigned_abs() as u32;
+ let _ = a.unsigned_abs();
+ let _ = a.unsigned_abs() as u128;
+
+ let a: isize = -3;
+ let _ = a.unsigned_abs();
+ let _ = a.unsigned_abs() as u8;
+ let _ = a.unsigned_abs() as u16;
+ let _ = a.unsigned_abs() as u32;
+ let _ = a.unsigned_abs() as u64;
+ let _ = a.unsigned_abs() as u128;
+}
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
new file mode 100644
index 000000000..110fbc6c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+ let x: i32 = -42;
+ let y: u32 = x.abs() as u32;
+ println!("The absolute value of {} is {}", x, y);
+
+ let a: i32 = -3;
+ let _: usize = a.abs() as usize;
+ let _: usize = a.abs() as _;
+ let _ = a.abs() as usize;
+
+ let a: i64 = -3;
+ let _ = a.abs() as usize;
+ let _ = a.abs() as u8;
+ let _ = a.abs() as u16;
+ let _ = a.abs() as u32;
+ let _ = a.abs() as u64;
+ let _ = a.abs() as u128;
+
+ let a: isize = -3;
+ let _ = a.abs() as usize;
+ let _ = a.abs() as u8;
+ let _ = a.abs() as u16;
+ let _ = a.abs() as u32;
+ let _ = a.abs() as u64;
+ let _ = a.abs() as u128;
+}
diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
new file mode 100644
index 000000000..02c24e106
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
@@ -0,0 +1,100 @@
+error: casting the result of `i32::abs()` to u32
+ --> $DIR/cast_abs_to_unsigned.rs:6:18
+ |
+LL | let y: u32 = x.abs() as u32;
+ | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
+ |
+ = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
+
+error: casting the result of `i32::abs()` to usize
+ --> $DIR/cast_abs_to_unsigned.rs:10:20
+ |
+LL | let _: usize = a.abs() as usize;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i32::abs()` to usize
+ --> $DIR/cast_abs_to_unsigned.rs:11:20
+ |
+LL | let _: usize = a.abs() as _;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i32::abs()` to usize
+ --> $DIR/cast_abs_to_unsigned.rs:12:13
+ |
+LL | let _ = a.abs() as usize;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to usize
+ --> $DIR/cast_abs_to_unsigned.rs:15:13
+ |
+LL | let _ = a.abs() as usize;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to u8
+ --> $DIR/cast_abs_to_unsigned.rs:16:13
+ |
+LL | let _ = a.abs() as u8;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to u16
+ --> $DIR/cast_abs_to_unsigned.rs:17:13
+ |
+LL | let _ = a.abs() as u16;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to u32
+ --> $DIR/cast_abs_to_unsigned.rs:18:13
+ |
+LL | let _ = a.abs() as u32;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to u64
+ --> $DIR/cast_abs_to_unsigned.rs:19:13
+ |
+LL | let _ = a.abs() as u64;
+ | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `i64::abs()` to u128
+ --> $DIR/cast_abs_to_unsigned.rs:20:13
+ |
+LL | let _ = a.abs() as u128;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to usize
+ --> $DIR/cast_abs_to_unsigned.rs:23:13
+ |
+LL | let _ = a.abs() as usize;
+ | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to u8
+ --> $DIR/cast_abs_to_unsigned.rs:24:13
+ |
+LL | let _ = a.abs() as u8;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to u16
+ --> $DIR/cast_abs_to_unsigned.rs:25:13
+ |
+LL | let _ = a.abs() as u16;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to u32
+ --> $DIR/cast_abs_to_unsigned.rs:26:13
+ |
+LL | let _ = a.abs() as u32;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to u64
+ --> $DIR/cast_abs_to_unsigned.rs:27:13
+ |
+LL | let _ = a.abs() as u64;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: casting the result of `isize::abs()` to u128
+ --> $DIR/cast_abs_to_unsigned.rs:28:13
+ |
+LL | let _ = a.abs() as u128;
+ | ^^^^^^^ help: replace with: `a.unsigned_abs()`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs
new file mode 100644
index 000000000..95bb883df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_alignment.rs
@@ -0,0 +1,51 @@
+//! Test casts for alignment issues
+
+#![feature(rustc_private)]
+#![feature(core_intrinsics)]
+extern crate libc;
+
+#[warn(clippy::cast_ptr_alignment)]
+#[allow(
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::cast_lossless,
+ clippy::borrow_as_ptr
+)]
+
+fn main() {
+ /* These should be warned against */
+
+ // cast to more-strictly-aligned type
+ (&1u8 as *const u8) as *const u16;
+ (&mut 1u8 as *mut u8) as *mut u16;
+
+ // cast to more-strictly-aligned type, but with the `pointer::cast` function.
+ (&1u8 as *const u8).cast::<u16>();
+ (&mut 1u8 as *mut u8).cast::<u16>();
+
+ /* These should be ok */
+
+ // not a pointer type
+ 1u8 as u16;
+ // cast to less-strictly-aligned type
+ (&1u16 as *const u16) as *const u8;
+ (&mut 1u16 as *mut u16) as *mut u8;
+ // For c_void, we should trust the user. See #2677
+ (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32;
+ (&1u32 as *const u32 as *const libc::c_void) as *const u32;
+ // For ZST, we should trust the user. See #4256
+ (&1u32 as *const u32 as *const ()) as *const u32;
+
+ // Issue #2881
+ let mut data = [0u8, 0u8];
+ unsafe {
+ let ptr = &data as *const [u8; 2] as *const u8;
+ let _ = (ptr as *const u16).read_unaligned();
+ let _ = core::ptr::read_unaligned(ptr as *const u16);
+ let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
+ let ptr = &mut data as *mut [u8; 2] as *mut u8;
+ (ptr as *mut u16).write_unaligned(0);
+ core::ptr::write_unaligned(ptr as *mut u16, 0);
+ core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr
new file mode 100644
index 000000000..5df2b5b10
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_alignment.stderr
@@ -0,0 +1,28 @@
+error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
+ --> $DIR/cast_alignment.rs:19:5
+ |
+LL | (&1u8 as *const u8) as *const u16;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings`
+
+error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
+ --> $DIR/cast_alignment.rs:20:5
+ |
+LL | (&mut 1u8 as *mut u8) as *mut u16;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
+ --> $DIR/cast_alignment.rs:23:5
+ |
+LL | (&1u8 as *const u8).cast::<u16>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
+ --> $DIR/cast_alignment.rs:24:5
+ |
+LL | (&mut 1u8 as *mut u8).cast::<u16>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.rs b/src/tools/clippy/tests/ui/cast_enum_constructor.rs
new file mode 100644
index 000000000..0193454ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_enum_constructor.rs
@@ -0,0 +1,17 @@
+#![warn(clippy::cast_enum_constructor)]
+#![allow(clippy::fn_to_numeric_cast)]
+
+fn main() {
+ enum Foo {
+ Y(u32),
+ }
+
+ enum Bar {
+ X,
+ }
+
+ let _ = Foo::Y as usize;
+ let _ = Foo::Y as isize;
+ let _ = Foo::Y as fn(u32) -> Foo;
+ let _ = Bar::X as usize;
+}
diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.stderr b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr
new file mode 100644
index 000000000..710909dd2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_enum_constructor.stderr
@@ -0,0 +1,16 @@
+error: cast of an enum tuple constructor to an integer
+ --> $DIR/cast_enum_constructor.rs:13:13
+ |
+LL | let _ = Foo::Y as usize;
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-enum-constructor` implied by `-D warnings`
+
+error: cast of an enum tuple constructor to an integer
+ --> $DIR/cast_enum_constructor.rs:14:13
+ |
+LL | let _ = Foo::Y as isize;
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
new file mode 100644
index 000000000..9e2da45c3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = u8::from(true);
+ let _ = u16::from(true);
+ let _ = u32::from(true);
+ let _ = u64::from(true);
+ let _ = u128::from(true);
+ let _ = usize::from(true);
+
+ let _ = i8::from(true);
+ let _ = i16::from(true);
+ let _ = i32::from(true);
+ let _ = i64::from(true);
+ let _ = i128::from(true);
+ let _ = isize::from(true);
+
+ // Test with an expression wrapped in parens
+ let _ = u16::from(true | false);
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: bool) -> u64 {
+ x as u64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.rs b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
new file mode 100644
index 000000000..b6f6c59a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = true as u8;
+ let _ = true as u16;
+ let _ = true as u32;
+ let _ = true as u64;
+ let _ = true as u128;
+ let _ = true as usize;
+
+ let _ = true as i8;
+ let _ = true as i16;
+ let _ = true as i32;
+ let _ = true as i64;
+ let _ = true as i128;
+ let _ = true as isize;
+
+ // Test with an expression wrapped in parens
+ let _ = (true | false) as u16;
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: bool) -> u64 {
+ x as u64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
new file mode 100644
index 000000000..6b1483360
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
@@ -0,0 +1,82 @@
+error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
+ --> $DIR/cast_lossless_bool.rs:8:13
+ |
+LL | let _ = true as u8;
+ | ^^^^^^^^^^ help: try: `u8::from(true)`
+ |
+ = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:9:13
+ |
+LL | let _ = true as u16;
+ | ^^^^^^^^^^^ help: try: `u16::from(true)`
+
+error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
+ --> $DIR/cast_lossless_bool.rs:10:13
+ |
+LL | let _ = true as u32;
+ | ^^^^^^^^^^^ help: try: `u32::from(true)`
+
+error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
+ --> $DIR/cast_lossless_bool.rs:11:13
+ |
+LL | let _ = true as u64;
+ | ^^^^^^^^^^^ help: try: `u64::from(true)`
+
+error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
+ --> $DIR/cast_lossless_bool.rs:12:13
+ |
+LL | let _ = true as u128;
+ | ^^^^^^^^^^^^ help: try: `u128::from(true)`
+
+error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
+ --> $DIR/cast_lossless_bool.rs:13:13
+ |
+LL | let _ = true as usize;
+ | ^^^^^^^^^^^^^ help: try: `usize::from(true)`
+
+error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
+ --> $DIR/cast_lossless_bool.rs:15:13
+ |
+LL | let _ = true as i8;
+ | ^^^^^^^^^^ help: try: `i8::from(true)`
+
+error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:16:13
+ |
+LL | let _ = true as i16;
+ | ^^^^^^^^^^^ help: try: `i16::from(true)`
+
+error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
+ --> $DIR/cast_lossless_bool.rs:17:13
+ |
+LL | let _ = true as i32;
+ | ^^^^^^^^^^^ help: try: `i32::from(true)`
+
+error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
+ --> $DIR/cast_lossless_bool.rs:18:13
+ |
+LL | let _ = true as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(true)`
+
+error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
+ --> $DIR/cast_lossless_bool.rs:19:13
+ |
+LL | let _ = true as i128;
+ | ^^^^^^^^^^^^ help: try: `i128::from(true)`
+
+error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
+ --> $DIR/cast_lossless_bool.rs:20:13
+ |
+LL | let _ = true as isize;
+ | ^^^^^^^^^^^^^ help: try: `isize::from(true)`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:23:13
+ |
+LL | let _ = (true | false) as u16;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.fixed b/src/tools/clippy/tests/ui/cast_lossless_float.fixed
new file mode 100644
index 000000000..32a9c1c4a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_float.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+
+#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to floating-point types
+ let x0 = 1i8;
+ let _ = f32::from(x0);
+ let _ = f64::from(x0);
+ let x1 = 1u8;
+ let _ = f32::from(x1);
+ let _ = f64::from(x1);
+ let x2 = 1i16;
+ let _ = f32::from(x2);
+ let _ = f64::from(x2);
+ let x3 = 1u16;
+ let _ = f32::from(x3);
+ let _ = f64::from(x3);
+ let x4 = 1i32;
+ let _ = f64::from(x4);
+ let x5 = 1u32;
+ let _ = f64::from(x5);
+
+ // Test with casts from floating-point types
+ let _ = f64::from(1.0f32);
+}
+
+// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: f32) -> f64 {
+ input as f64
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: f32) -> f64 {
+ x as f64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.rs b/src/tools/clippy/tests/ui/cast_lossless_float.rs
new file mode 100644
index 000000000..6f5ddcfe0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_float.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+
+#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to floating-point types
+ let x0 = 1i8;
+ let _ = x0 as f32;
+ let _ = x0 as f64;
+ let x1 = 1u8;
+ let _ = x1 as f32;
+ let _ = x1 as f64;
+ let x2 = 1i16;
+ let _ = x2 as f32;
+ let _ = x2 as f64;
+ let x3 = 1u16;
+ let _ = x3 as f32;
+ let _ = x3 as f64;
+ let x4 = 1i32;
+ let _ = x4 as f64;
+ let x5 = 1u32;
+ let _ = x5 as f64;
+
+ // Test with casts from floating-point types
+ let _ = 1.0f32 as f64;
+}
+
+// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: f32) -> f64 {
+ input as f64
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: f32) -> f64 {
+ x as f64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr
new file mode 100644
index 000000000..8326d40be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr
@@ -0,0 +1,70 @@
+error: casting `i8` to `f32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:9:13
+ |
+LL | let _ = x0 as f32;
+ | ^^^^^^^^^ help: try: `f32::from(x0)`
+ |
+ = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `i8` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:10:13
+ |
+LL | let _ = x0 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x0)`
+
+error: casting `u8` to `f32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:12:13
+ |
+LL | let _ = x1 as f32;
+ | ^^^^^^^^^ help: try: `f32::from(x1)`
+
+error: casting `u8` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:13:13
+ |
+LL | let _ = x1 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x1)`
+
+error: casting `i16` to `f32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:15:13
+ |
+LL | let _ = x2 as f32;
+ | ^^^^^^^^^ help: try: `f32::from(x2)`
+
+error: casting `i16` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:16:13
+ |
+LL | let _ = x2 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x2)`
+
+error: casting `u16` to `f32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:18:13
+ |
+LL | let _ = x3 as f32;
+ | ^^^^^^^^^ help: try: `f32::from(x3)`
+
+error: casting `u16` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:19:13
+ |
+LL | let _ = x3 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x3)`
+
+error: casting `i32` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:21:13
+ |
+LL | let _ = x4 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x4)`
+
+error: casting `u32` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:23:13
+ |
+LL | let _ = x5 as f64;
+ | ^^^^^^^^^ help: try: `f64::from(x5)`
+
+error: casting `f32` to `f64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_float.rs:26:13
+ |
+LL | let _ = 1.0f32 as f64;
+ | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed
new file mode 100644
index 000000000..72a708b40
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+
+#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = i16::from(1i8);
+ let _ = i32::from(1i8);
+ let _ = i64::from(1i8);
+ let _ = i16::from(1u8);
+ let _ = i32::from(1u8);
+ let _ = i64::from(1u8);
+ let _ = u16::from(1u8);
+ let _ = u32::from(1u8);
+ let _ = u64::from(1u8);
+ let _ = i32::from(1i16);
+ let _ = i64::from(1i16);
+ let _ = i32::from(1u16);
+ let _ = i64::from(1u16);
+ let _ = u32::from(1u16);
+ let _ = u64::from(1u16);
+ let _ = i64::from(1i32);
+ let _ = i64::from(1u32);
+ let _ = u64::from(1u32);
+
+ // Test with an expression wrapped in parens
+ let _ = u16::from(1u8 + 1u8);
+}
+
+// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: u16) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: u32) -> u64 {
+ x as u64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.rs b/src/tools/clippy/tests/ui/cast_lossless_integer.rs
new file mode 100644
index 000000000..34bb47181
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_integer.rs
@@ -0,0 +1,47 @@
+// run-rustfix
+
+#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = 1i8 as i16;
+ let _ = 1i8 as i32;
+ let _ = 1i8 as i64;
+ let _ = 1u8 as i16;
+ let _ = 1u8 as i32;
+ let _ = 1u8 as i64;
+ let _ = 1u8 as u16;
+ let _ = 1u8 as u32;
+ let _ = 1u8 as u64;
+ let _ = 1i16 as i32;
+ let _ = 1i16 as i64;
+ let _ = 1u16 as i32;
+ let _ = 1u16 as i64;
+ let _ = 1u16 as u32;
+ let _ = 1u16 as u64;
+ let _ = 1i32 as i64;
+ let _ = 1u32 as i64;
+ let _ = 1u32 as u64;
+
+ // Test with an expression wrapped in parens
+ let _ = (1u8 + 1u8) as u16;
+}
+
+// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: u16) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: u32) -> u64 {
+ x as u64
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr
new file mode 100644
index 000000000..721b94876
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr
@@ -0,0 +1,118 @@
+error: casting `i8` to `i16` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:8:13
+ |
+LL | let _ = 1i8 as i16;
+ | ^^^^^^^^^^ help: try: `i16::from(1i8)`
+ |
+ = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `i8` to `i32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:9:13
+ |
+LL | let _ = 1i8 as i32;
+ | ^^^^^^^^^^ help: try: `i32::from(1i8)`
+
+error: casting `i8` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:10:13
+ |
+LL | let _ = 1i8 as i64;
+ | ^^^^^^^^^^ help: try: `i64::from(1i8)`
+
+error: casting `u8` to `i16` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:11:13
+ |
+LL | let _ = 1u8 as i16;
+ | ^^^^^^^^^^ help: try: `i16::from(1u8)`
+
+error: casting `u8` to `i32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:12:13
+ |
+LL | let _ = 1u8 as i32;
+ | ^^^^^^^^^^ help: try: `i32::from(1u8)`
+
+error: casting `u8` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:13:13
+ |
+LL | let _ = 1u8 as i64;
+ | ^^^^^^^^^^ help: try: `i64::from(1u8)`
+
+error: casting `u8` to `u16` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:14:13
+ |
+LL | let _ = 1u8 as u16;
+ | ^^^^^^^^^^ help: try: `u16::from(1u8)`
+
+error: casting `u8` to `u32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:15:13
+ |
+LL | let _ = 1u8 as u32;
+ | ^^^^^^^^^^ help: try: `u32::from(1u8)`
+
+error: casting `u8` to `u64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:16:13
+ |
+LL | let _ = 1u8 as u64;
+ | ^^^^^^^^^^ help: try: `u64::from(1u8)`
+
+error: casting `i16` to `i32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:17:13
+ |
+LL | let _ = 1i16 as i32;
+ | ^^^^^^^^^^^ help: try: `i32::from(1i16)`
+
+error: casting `i16` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:18:13
+ |
+LL | let _ = 1i16 as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(1i16)`
+
+error: casting `u16` to `i32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:19:13
+ |
+LL | let _ = 1u16 as i32;
+ | ^^^^^^^^^^^ help: try: `i32::from(1u16)`
+
+error: casting `u16` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:20:13
+ |
+LL | let _ = 1u16 as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(1u16)`
+
+error: casting `u16` to `u32` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:21:13
+ |
+LL | let _ = 1u16 as u32;
+ | ^^^^^^^^^^^ help: try: `u32::from(1u16)`
+
+error: casting `u16` to `u64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:22:13
+ |
+LL | let _ = 1u16 as u64;
+ | ^^^^^^^^^^^ help: try: `u64::from(1u16)`
+
+error: casting `i32` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:23:13
+ |
+LL | let _ = 1i32 as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(1i32)`
+
+error: casting `u32` to `i64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:24:13
+ |
+LL | let _ = 1u32 as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(1u32)`
+
+error: casting `u32` to `u64` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:25:13
+ |
+LL | let _ = 1u32 as u64;
+ | ^^^^^^^^^^^ help: try: `u64::from(1u32)`
+
+error: casting `u8` to `u16` may become silently lossy if you later change the type
+ --> $DIR/cast_lossless_integer.rs:28:13
+ |
+LL | let _ = (1u8 + 1u8) as u16;
+ | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs
new file mode 100644
index 000000000..c48a734ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs
@@ -0,0 +1,31 @@
+#![warn(clippy::cast_ref_to_mut)]
+#![allow(clippy::no_effect, clippy::borrow_as_ptr)]
+
+extern "C" {
+ // N.B., mutability can be easily incorrect in FFI calls -- as
+ // in C, the default is mutable pointers.
+ fn ffi(c: *mut u8);
+ fn int_ffi(c: *mut i32);
+}
+
+fn main() {
+ let s = String::from("Hello");
+ let a = &s;
+ unsafe {
+ let num = &3i32;
+ let mut_num = &mut 3i32;
+ // Should be warned against
+ (*(a as *const _ as *mut String)).push_str(" world");
+ *(a as *const _ as *mut _) = String::from("Replaced");
+ *(a as *const _ as *mut String) += " world";
+ // Shouldn't be warned against
+ println!("{}", *(num as *const _ as *const i16));
+ println!("{}", *(mut_num as *mut _ as *mut i16));
+ ffi(a.as_ptr() as *mut _);
+ int_ffi(num as *const _ as *mut _);
+ int_ffi(&3 as *const _ as *mut _);
+ let mut value = 3;
+ let value: *const i32 = &mut value;
+ *(value as *const i16 as *mut i16) = 42;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr
new file mode 100644
index 000000000..aacd99437
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr
@@ -0,0 +1,22 @@
+error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
+ --> $DIR/cast_ref_to_mut.rs:18:9
+ |
+LL | (*(a as *const _ as *mut String)).push_str(" world");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings`
+
+error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
+ --> $DIR/cast_ref_to_mut.rs:19:9
+ |
+LL | *(a as *const _ as *mut _) = String::from("Replaced");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
+ --> $DIR/cast_ref_to_mut.rs:20:9
+ |
+LL | *(a as *const _ as *mut String) += " world";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs
new file mode 100644
index 000000000..595109be4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_size.rs
@@ -0,0 +1,35 @@
+// ignore-32bit
+#[warn(
+ clippy::cast_precision_loss,
+ clippy::cast_possible_truncation,
+ clippy::cast_sign_loss,
+ clippy::cast_possible_wrap,
+ clippy::cast_lossless
+)]
+#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+fn main() {
+ // Casting from *size
+ 1isize as i8;
+ let x0 = 1isize;
+ let x1 = 1usize;
+ x0 as f64;
+ x1 as f64;
+ x0 as f32;
+ x1 as f32;
+ 1isize as i32;
+ 1isize as u32;
+ 1usize as u32;
+ 1usize as i32;
+ // Casting to *size
+ 1i64 as isize;
+ 1i64 as usize;
+ 1u64 as isize;
+ 1u64 as usize;
+ 1u32 as isize;
+ 1u32 as usize; // Should not trigger any lint
+ 1i32 as isize; // Neither should this
+ 1i32 as usize;
+ // Big integer literal to float
+ 999_999_999 as f32;
+ 9_999_999_999_999_999usize as f64;
+}
diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.stderr
new file mode 100644
index 000000000..95552f2e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_size.stderr
@@ -0,0 +1,116 @@
+error: casting `isize` to `i8` may truncate the value
+ --> $DIR/cast_size.rs:12:5
+ |
+LL | 1isize as i8;
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+
+error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size.rs:15:5
+ |
+LL | x0 as f64;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size.rs:16:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:17:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+
+error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:18:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:19:5
+ |
+LL | 1isize as i32;
+ | ^^^^^^^^^^^^^
+
+error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:20:5
+ |
+LL | 1isize as u32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:21:5
+ |
+LL | 1usize as u32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:22:5
+ |
+LL | 1usize as i32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:22:5
+ |
+LL | 1usize as i32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+
+error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:24:5
+ |
+LL | 1i64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:25:5
+ |
+LL | 1i64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:26:5
+ |
+LL | 1u64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:26:5
+ |
+LL | 1u64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:27:5
+ |
+LL | 1u64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:28:5
+ |
+LL | 1u32 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:33:5
+ |
+LL | 999_999_999 as f32;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size.rs:34:5
+ |
+LL | 9_999_999_999_999_999usize as f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.rs b/src/tools/clippy/tests/ui/cast_size_32bit.rs
new file mode 100644
index 000000000..99aac6dec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_size_32bit.rs
@@ -0,0 +1,35 @@
+// ignore-64bit
+#[warn(
+ clippy::cast_precision_loss,
+ clippy::cast_possible_truncation,
+ clippy::cast_sign_loss,
+ clippy::cast_possible_wrap,
+ clippy::cast_lossless
+)]
+#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+fn main() {
+ // Casting from *size
+ 1isize as i8;
+ let x0 = 1isize;
+ let x1 = 1usize;
+ x0 as f64;
+ x1 as f64;
+ x0 as f32;
+ x1 as f32;
+ 1isize as i32;
+ 1isize as u32;
+ 1usize as u32;
+ 1usize as i32;
+ // Casting to *size
+ 1i64 as isize;
+ 1i64 as usize;
+ 1u64 as isize;
+ 1u64 as usize;
+ 1u32 as isize;
+ 1u32 as usize; // Should not trigger any lint
+ 1i32 as isize; // Neither should this
+ 1i32 as usize;
+ // Big integer literal to float
+ 999_999_999 as f32;
+ 3_999_999_999usize as f64;
+}
diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size_32bit.stderr
new file mode 100644
index 000000000..8990c3ba7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_size_32bit.stderr
@@ -0,0 +1,118 @@
+error: casting `isize` to `i8` may truncate the value
+ --> $DIR/cast_size_32bit.rs:12:5
+ |
+LL | 1isize as i8;
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+
+error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size_32bit.rs:15:5
+ |
+LL | x0 as f64;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size_32bit.rs:16:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size_32bit.rs:17:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+
+error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size_32bit.rs:18:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:19:5
+ |
+LL | 1isize as i32;
+ | ^^^^^^^^^^^^^
+
+error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:20:5
+ |
+LL | 1isize as u32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:21:5
+ |
+LL | 1usize as u32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:22:5
+ |
+LL | 1usize as i32;
+ | ^^^^^^^^^^^^^
+
+error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:22:5
+ |
+LL | 1usize as i32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+
+error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:24:5
+ |
+LL | 1i64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:25:5
+ |
+LL | 1i64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:26:5
+ |
+LL | 1u64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:26:5
+ |
+LL | 1u64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:27:5
+ |
+LL | 1u64 as usize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size_32bit.rs:28:5
+ |
+LL | 1u32 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size_32bit.rs:33:5
+ |
+LL | 999_999_999 as f32;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/cast_size_32bit.rs:34:5
+ |
+LL | 3_999_999_999usize as f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64`
+ |
+ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
new file mode 100644
index 000000000..24d7eb28a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
@@ -0,0 +1,82 @@
+#![allow(clippy::let_unit_value)]
+
+fn main() {
+ let x: [i32; 3] = [1_i32, 2, 3];
+ let r_x = &x;
+ // Check casting through multiple bindings
+ // Because it's separate, it does not check the cast back to something of the same size
+ let a = r_x as *const [i32];
+ let b = a as *const [u8];
+ let c = b as *const [u32];
+
+ // loses data
+ let loss = r_x as *const [i32] as *const [u8];
+
+ // Cast back to same size but different type loses no data, just type conversion
+ // This is weird code but there's no reason for this lint specifically to fire *twice* on it
+ let restore = r_x as *const [i32] as *const [u8] as *const [u32];
+
+ // Check casting through blocks is detected
+ let loss_block_1 = { r_x as *const [i32] } as *const [u8];
+ let loss_block_2 = {
+ let _ = ();
+ r_x as *const [i32]
+ } as *const [u8];
+
+ // Check that resores of the same size are detected through blocks
+ let restore_block_1 = { r_x as *const [i32] } as *const [u8] as *const [u32];
+ let restore_block_2 = { ({ r_x as *const [i32] }) as *const [u8] } as *const [u32];
+ let restore_block_3 = {
+ let _ = ();
+ ({
+ let _ = ();
+ r_x as *const [i32]
+ }) as *const [u8]
+ } as *const [u32];
+
+ // Check that the result of a long chain of casts is detected
+ let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
+ let long_chain_restore =
+ r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32];
+}
+
+// foo and foo2 should not fire, they're the same size
+fn foo(x: *mut [u8]) -> *mut [u8] {
+ x as *mut [u8]
+}
+
+fn foo2(x: *mut [u8]) -> *mut [u8] {
+ x as *mut _
+}
+
+// Test that casts as part of function returns work
+fn bar(x: *mut [u16]) -> *mut [u8] {
+ x as *mut [u8]
+}
+
+fn uwu(x: *mut [u16]) -> *mut [u8] {
+ x as *mut _
+}
+
+fn bar2(x: *mut [u16]) -> *mut [u8] {
+ x as _
+}
+
+// constify
+fn bar3(x: *mut [u16]) -> *const [u8] {
+ x as _
+}
+
+// unconstify
+fn bar4(x: *const [u16]) -> *mut [u8] {
+ x as _
+}
+
+// function returns plus blocks
+fn blocks(x: *mut [u16]) -> *mut [u8] {
+ ({ x }) as _
+}
+
+fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+ { ({ x }) as _ }
+}
diff --git a/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr b/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr
new file mode 100644
index 000000000..40721dcd0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr
@@ -0,0 +1,121 @@
+error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:9:13
+ |
+LL | let b = a as *const [u8];
+ | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
+ |
+ = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
+
+error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:10:13
+ |
+LL | let c = b as *const [u32];
+ | ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
+
+error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:13:16
+ |
+LL | let loss = r_x as *const [i32] as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
+
+error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:20:24
+ |
+LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
+
+error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:21:24
+ |
+LL | let loss_block_2 = {
+ | ________________________^
+LL | | let _ = ();
+LL | | r_x as *const [i32]
+LL | | } as *const [u8];
+ | |____________________^
+ |
+help: replace with `ptr::slice_from_raw_parts`
+ |
+LL ~ let loss_block_2 = core::ptr::slice_from_raw_parts({
+LL + let _ = ();
+LL + r_x as *const [i32]
+LL ~ } as *const u8, ..);
+ |
+
+error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:38:27
+ |
+LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:53:36
+ |
+LL | fn bar(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________^
+LL | | x as *mut [u8]
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:57:36
+ |
+LL | fn uwu(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________^
+LL | | x as *mut _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:61:37
+ |
+LL | fn bar2(x: *mut [u16]) -> *mut [u8] {
+ | _____________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:66:39
+ |
+LL | fn bar3(x: *mut [u16]) -> *const [u8] {
+ | _______________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:71:39
+ |
+LL | fn bar4(x: *const [u16]) -> *mut [u8] {
+ | _______________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:76:39
+ |
+LL | fn blocks(x: *mut [u16]) -> *mut [u8] {
+ | _______________________________________^
+LL | | ({ x }) as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:80:44
+ |
+LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________________^
+LL | | { ({ x }) as _ }
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:81:5
+ |
+LL | { ({ x }) as _ }
+ | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
new file mode 100644
index 000000000..061a4ab9b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+
+#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
+#![warn(clippy::deprecated_cfg_attr)]
+
+// This doesn't get linted, see known problems
+#![cfg_attr(rustfmt, rustfmt_skip)]
+
+#[rustfmt::skip]
+trait Foo
+{
+fn foo(
+);
+}
+
+fn skip_on_statements() {
+ #[rustfmt::skip]
+ 5+3;
+}
+
+#[rustfmt::skip]
+fn main() {
+ foo::f();
+}
+
+mod foo {
+ #![cfg_attr(rustfmt, rustfmt_skip)]
+
+ pub fn f() {}
+}
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
new file mode 100644
index 000000000..035169fab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+
+#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
+#![warn(clippy::deprecated_cfg_attr)]
+
+// This doesn't get linted, see known problems
+#![cfg_attr(rustfmt, rustfmt_skip)]
+
+#[rustfmt::skip]
+trait Foo
+{
+fn foo(
+);
+}
+
+fn skip_on_statements() {
+ #[cfg_attr(rustfmt, rustfmt::skip)]
+ 5+3;
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+fn main() {
+ foo::f();
+}
+
+mod foo {
+ #![cfg_attr(rustfmt, rustfmt_skip)]
+
+ pub fn f() {}
+}
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
new file mode 100644
index 000000000..c1efd47db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
@@ -0,0 +1,16 @@
+error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
+ --> $DIR/cfg_attr_rustfmt.rs:18:5
+ |
+LL | #[cfg_attr(rustfmt, rustfmt::skip)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
+ |
+ = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings`
+
+error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
+ --> $DIR/cfg_attr_rustfmt.rs:22:1
+ |
+LL | #[cfg_attr(rustfmt, rustfmt_skip)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.rs b/src/tools/clippy/tests/ui/char_lit_as_u8.rs
new file mode 100644
index 000000000..0a53a3d64
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8.rs
@@ -0,0 +1,5 @@
+#![warn(clippy::char_lit_as_u8)]
+
+fn main() {
+ let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
+}
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr
new file mode 100644
index 000000000..b9836d2f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr
@@ -0,0 +1,11 @@
+error: casting a character literal to `u8` truncates
+ --> $DIR/char_lit_as_u8.rs:4:13
+ |
+LL | let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
+ = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed
new file mode 100644
index 000000000..3dc3cb4e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::char_lit_as_u8)]
+
+fn main() {
+ let _ = b'a';
+ let _ = b'\n';
+ let _ = b'\0';
+ let _ = b'\x01';
+}
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs
new file mode 100644
index 000000000..d379a0234
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::char_lit_as_u8)]
+
+fn main() {
+ let _ = 'a' as u8;
+ let _ = '\n' as u8;
+ let _ = '\0' as u8;
+ let _ = '\x01' as u8;
+}
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr
new file mode 100644
index 000000000..bf7cb1607
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr
@@ -0,0 +1,35 @@
+error: casting a character literal to `u8` truncates
+ --> $DIR/char_lit_as_u8_suggestions.rs:6:13
+ |
+LL | let _ = 'a' as u8;
+ | ^^^^^^^^^ help: use a byte literal instead: `b'a'`
+ |
+ = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
+ = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: casting a character literal to `u8` truncates
+ --> $DIR/char_lit_as_u8_suggestions.rs:7:13
+ |
+LL | let _ = '/n' as u8;
+ | ^^^^^^^^^^ help: use a byte literal instead: `b'/n'`
+ |
+ = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: casting a character literal to `u8` truncates
+ --> $DIR/char_lit_as_u8_suggestions.rs:8:13
+ |
+LL | let _ = '/0' as u8;
+ | ^^^^^^^^^^ help: use a byte literal instead: `b'/0'`
+ |
+ = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: casting a character literal to `u8` truncates
+ --> $DIR/char_lit_as_u8_suggestions.rs:9:13
+ |
+LL | let _ = '/x01' as u8;
+ | ^^^^^^^^^^^^ help: use a byte literal instead: `b'/x01'`
+ |
+ = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed
new file mode 100644
index 000000000..cb7100bc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_conversions.fixed
@@ -0,0 +1,79 @@
+// run-rustfix
+
+#![allow(
+ clippy::cast_lossless,
+ // Int::max_value will be deprecated in the future
+ deprecated,
+)]
+#![warn(clippy::checked_conversions)]
+
+// Positive tests
+
+// Signed to unsigned
+
+pub fn i64_to_u32(value: i64) {
+ let _ = u32::try_from(value).is_ok();
+ let _ = u32::try_from(value).is_ok();
+}
+
+pub fn i64_to_u16(value: i64) {
+ let _ = u16::try_from(value).is_ok();
+ let _ = u16::try_from(value).is_ok();
+}
+
+pub fn isize_to_u8(value: isize) {
+ let _ = u8::try_from(value).is_ok();
+ let _ = u8::try_from(value).is_ok();
+}
+
+// Signed to signed
+
+pub fn i64_to_i32(value: i64) {
+ let _ = i32::try_from(value).is_ok();
+ let _ = i32::try_from(value).is_ok();
+}
+
+pub fn i64_to_i16(value: i64) {
+ let _ = i16::try_from(value).is_ok();
+ let _ = i16::try_from(value).is_ok();
+}
+
+// Unsigned to X
+
+pub fn u32_to_i32(value: u32) {
+ let _ = i32::try_from(value).is_ok();
+ let _ = i32::try_from(value).is_ok();
+}
+
+pub fn usize_to_isize(value: usize) {
+ let _ = isize::try_from(value).is_ok() && value as i32 == 5;
+ let _ = isize::try_from(value).is_ok() && value as i32 == 5;
+}
+
+pub fn u32_to_u16(value: u32) {
+ let _ = u16::try_from(value).is_ok() && value as i32 == 5;
+ let _ = u16::try_from(value).is_ok() && value as i32 == 5;
+}
+
+// Negative tests
+
+pub fn no_i64_to_i32(value: i64) {
+ let _ = value <= (i32::max_value() as i64) && value >= 0;
+ let _ = value <= (i32::MAX as i64) && value >= 0;
+}
+
+pub fn no_isize_to_u8(value: isize) {
+ let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
+ let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
+}
+
+pub fn i8_to_u8(value: i8) {
+ let _ = value >= 0;
+}
+
+// Do not lint
+pub const fn issue_8898(i: u32) -> bool {
+ i <= i32::MAX as u32
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs
new file mode 100644
index 000000000..ed4e06923
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_conversions.rs
@@ -0,0 +1,79 @@
+// run-rustfix
+
+#![allow(
+ clippy::cast_lossless,
+ // Int::max_value will be deprecated in the future
+ deprecated,
+)]
+#![warn(clippy::checked_conversions)]
+
+// Positive tests
+
+// Signed to unsigned
+
+pub fn i64_to_u32(value: i64) {
+ let _ = value <= (u32::max_value() as i64) && value >= 0;
+ let _ = value <= (u32::MAX as i64) && value >= 0;
+}
+
+pub fn i64_to_u16(value: i64) {
+ let _ = value <= i64::from(u16::max_value()) && value >= 0;
+ let _ = value <= i64::from(u16::MAX) && value >= 0;
+}
+
+pub fn isize_to_u8(value: isize) {
+ let _ = value <= (u8::max_value() as isize) && value >= 0;
+ let _ = value <= (u8::MAX as isize) && value >= 0;
+}
+
+// Signed to signed
+
+pub fn i64_to_i32(value: i64) {
+ let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
+ let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
+}
+
+pub fn i64_to_i16(value: i64) {
+ let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
+ let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
+}
+
+// Unsigned to X
+
+pub fn u32_to_i32(value: u32) {
+ let _ = value <= i32::max_value() as u32;
+ let _ = value <= i32::MAX as u32;
+}
+
+pub fn usize_to_isize(value: usize) {
+ let _ = value <= isize::max_value() as usize && value as i32 == 5;
+ let _ = value <= isize::MAX as usize && value as i32 == 5;
+}
+
+pub fn u32_to_u16(value: u32) {
+ let _ = value <= u16::max_value() as u32 && value as i32 == 5;
+ let _ = value <= u16::MAX as u32 && value as i32 == 5;
+}
+
+// Negative tests
+
+pub fn no_i64_to_i32(value: i64) {
+ let _ = value <= (i32::max_value() as i64) && value >= 0;
+ let _ = value <= (i32::MAX as i64) && value >= 0;
+}
+
+pub fn no_isize_to_u8(value: isize) {
+ let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
+ let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
+}
+
+pub fn i8_to_u8(value: i8) {
+ let _ = value >= 0;
+}
+
+// Do not lint
+pub const fn issue_8898(i: u32) -> bool {
+ i <= i32::MAX as u32
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr
new file mode 100644
index 000000000..2e5180405
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_conversions.stderr
@@ -0,0 +1,100 @@
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:15:13
+ |
+LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
+ |
+ = note: `-D clippy::checked-conversions` implied by `-D warnings`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:16:13
+ |
+LL | let _ = value <= (u32::MAX as i64) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:20:13
+ |
+LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:21:13
+ |
+LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:25:13
+ |
+LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:26:13
+ |
+LL | let _ = value <= (u8::MAX as isize) && value >= 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:32:13
+ |
+LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:33:13
+ |
+LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:37:13
+ |
+LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:38:13
+ |
+LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:44:13
+ |
+LL | let _ = value <= i32::max_value() as u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:45:13
+ |
+LL | let _ = value <= i32::MAX as u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:49:13
+ |
+LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:50:13
+ |
+LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:54:13
+ |
+LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
+
+error: checked cast can be simplified
+ --> $DIR/checked_conversions.rs:55:13
+ |
+LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
new file mode 100644
index 000000000..ec082c73b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
@@ -0,0 +1,54 @@
+#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+fn test_complex_conditions() {
+ let x: Result<(), ()> = Ok(());
+ let y: Result<(), ()> = Ok(());
+ if x.is_ok() && y.is_err() {
+ x.unwrap(); // unnecessary
+ x.unwrap_err(); // will panic
+ y.unwrap(); // will panic
+ y.unwrap_err(); // unnecessary
+ } else {
+ // not statically determinable whether any of the following will always succeed or always fail:
+ x.unwrap();
+ x.unwrap_err();
+ y.unwrap();
+ y.unwrap_err();
+ }
+
+ if x.is_ok() || y.is_ok() {
+ // not statically determinable whether any of the following will always succeed or always fail:
+ x.unwrap();
+ y.unwrap();
+ } else {
+ x.unwrap(); // will panic
+ x.unwrap_err(); // unnecessary
+ y.unwrap(); // will panic
+ y.unwrap_err(); // unnecessary
+ }
+ let z: Result<(), ()> = Ok(());
+ if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ x.unwrap(); // unnecessary
+ x.unwrap_err(); // will panic
+ y.unwrap(); // will panic
+ y.unwrap_err(); // unnecessary
+ z.unwrap(); // unnecessary
+ z.unwrap_err(); // will panic
+ }
+ if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ // not statically determinable whether any of the following will always succeed or always fail:
+ x.unwrap();
+ y.unwrap();
+ z.unwrap();
+ } else {
+ x.unwrap(); // will panic
+ x.unwrap_err(); // unnecessary
+ y.unwrap(); // unnecessary
+ y.unwrap_err(); // will panic
+ z.unwrap(); // will panic
+ z.unwrap_err(); // unnecessary
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
new file mode 100644
index 000000000..46c6f6970
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
@@ -0,0 +1,211 @@
+error: called `unwrap` on `x` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:8:9
+ |
+LL | if x.is_ok() && y.is_err() {
+ | --------- the check is happening here
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/complex_conditionals.rs:1:35
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/complex_conditionals.rs:9:9
+ |
+LL | if x.is_ok() && y.is_err() {
+ | --------- because of this check
+LL | x.unwrap(); // unnecessary
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/complex_conditionals.rs:1:9
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:10:9
+ |
+LL | if x.is_ok() && y.is_err() {
+ | ---------- because of this check
+...
+LL | y.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `y` after checking its variant with `is_err`
+ --> $DIR/complex_conditionals.rs:11:9
+ |
+LL | if x.is_ok() && y.is_err() {
+ | ---------- the check is happening here
+...
+LL | y.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:25:9
+ |
+LL | if x.is_ok() || y.is_ok() {
+ | --------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:26:9
+ |
+LL | if x.is_ok() || y.is_ok() {
+ | --------- the check is happening here
+...
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:27:9
+ |
+LL | if x.is_ok() || y.is_ok() {
+ | --------- because of this check
+...
+LL | y.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `y` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:28:9
+ |
+LL | if x.is_ok() || y.is_ok() {
+ | --------- the check is happening here
+...
+LL | y.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: called `unwrap` on `x` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:32:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | --------- the check is happening here
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/complex_conditionals.rs:33:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | --------- because of this check
+LL | x.unwrap(); // unnecessary
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:34:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | --------- because of this check
+...
+LL | y.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `y` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:35:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | --------- the check is happening here
+...
+LL | y.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: called `unwrap` on `z` after checking its variant with `is_err`
+ --> $DIR/complex_conditionals.rs:36:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | ---------- the check is happening here
+...
+LL | z.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/complex_conditionals.rs:37:9
+ |
+LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
+ | ---------- because of this check
+...
+LL | z.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:45:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | --------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:46:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | --------- the check is happening here
+...
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: called `unwrap` on `y` after checking its variant with `is_ok`
+ --> $DIR/complex_conditionals.rs:47:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | --------- the check is happening here
+...
+LL | y.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/complex_conditionals.rs:48:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | --------- because of this check
+...
+LL | y.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals.rs:49:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | ---------- because of this check
+...
+LL | z.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `z` after checking its variant with `is_err`
+ --> $DIR/complex_conditionals.rs:50:9
+ |
+LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
+ | ---------- the check is happening here
+...
+LL | z.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+ |
+ = help: try using `if let` or `match`
+
+error: aborting due to 20 previous errors
+
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
new file mode 100644
index 000000000..043ea4148
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
@@ -0,0 +1,15 @@
+#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+fn test_nested() {
+ fn nested() {
+ let x = Some(());
+ if x.is_some() {
+ x.unwrap(); // unnecessary
+ } else {
+ x.unwrap(); // will panic
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
new file mode 100644
index 000000000..542ab5330
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
@@ -0,0 +1,31 @@
+error: called `unwrap` on `x` after checking its variant with `is_some`
+ --> $DIR/complex_conditionals_nested.rs:8:13
+ |
+LL | if x.is_some() {
+ | -------------- help: try: `if let Some(..) = x`
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/complex_conditionals_nested.rs:1:35
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/complex_conditionals_nested.rs:10:13
+ |
+LL | if x.is_some() {
+ | ----------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/complex_conditionals_nested.rs:1:9
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
new file mode 100644
index 000000000..82dce8197
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -0,0 +1,102 @@
+#![feature(lint_reasons)]
+#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+macro_rules! m {
+ ($a:expr) => {
+ if $a.is_some() {
+ $a.unwrap(); // unnecessary
+ }
+ };
+}
+
+macro_rules! checks_in_param {
+ ($a:expr, $b:expr) => {
+ if $a {
+ $b;
+ }
+ };
+}
+
+macro_rules! checks_unwrap {
+ ($a:expr, $b:expr) => {
+ if $a.is_some() {
+ $b;
+ }
+ };
+}
+
+macro_rules! checks_some {
+ ($a:expr, $b:expr) => {
+ if $a {
+ $b.unwrap();
+ }
+ };
+}
+
+fn main() {
+ let x = Some(());
+ if x.is_some() {
+ x.unwrap(); // unnecessary
+ x.expect("an error message"); // unnecessary
+ } else {
+ x.unwrap(); // will panic
+ x.expect("an error message"); // will panic
+ }
+ if x.is_none() {
+ x.unwrap(); // will panic
+ } else {
+ x.unwrap(); // unnecessary
+ }
+ m!(x);
+ checks_in_param!(x.is_some(), x.unwrap()); // ok
+ checks_unwrap!(x, x.unwrap()); // ok
+ checks_some!(x.is_some(), x); // ok
+ let mut x: Result<(), ()> = Ok(());
+ if x.is_ok() {
+ x.unwrap(); // unnecessary
+ x.expect("an error message"); // unnecessary
+ x.unwrap_err(); // will panic
+ } else {
+ x.unwrap(); // will panic
+ x.expect("an error message"); // will panic
+ x.unwrap_err(); // unnecessary
+ }
+ if x.is_err() {
+ x.unwrap(); // will panic
+ x.unwrap_err(); // unnecessary
+ } else {
+ x.unwrap(); // unnecessary
+ x.unwrap_err(); // will panic
+ }
+ if x.is_ok() {
+ x = Err(());
+ // not unnecessary because of mutation of x
+ // it will always panic but the lint is not smart enough to see this (it only
+ // checks if conditions).
+ x.unwrap();
+ } else {
+ x = Ok(());
+ // not unnecessary because of mutation of x
+ // it will always panic but the lint is not smart enough to see this (it
+ // only checks if conditions).
+ x.unwrap_err();
+ }
+
+ assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
+}
+
+fn check_expect() {
+ let x = Some(());
+ if x.is_some() {
+ #[expect(clippy::unnecessary_unwrap)]
+ x.unwrap(); // unnecessary
+ #[expect(clippy::unnecessary_unwrap)]
+ x.expect("an error message"); // unnecessary
+ } else {
+ #[expect(clippy::panicking_unwrap)]
+ x.unwrap(); // will panic
+ #[expect(clippy::panicking_unwrap)]
+ x.expect("an error message"); // will panic
+ }
+}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
new file mode 100644
index 000000000..ef6882742
--- /dev/null
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -0,0 +1,167 @@
+error: called `unwrap` on `x` after checking its variant with `is_some`
+ --> $DIR/simple_conditionals.rs:40:9
+ |
+LL | if x.is_some() {
+ | -------------- help: try: `if let Some(..) = x`
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/simple_conditionals.rs:2:35
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `expect` on `x` after checking its variant with `is_some`
+ --> $DIR/simple_conditionals.rs:41:9
+ |
+LL | if x.is_some() {
+ | -------------- help: try: `if let Some(..) = x`
+LL | x.unwrap(); // unnecessary
+LL | x.expect("an error message"); // unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/simple_conditionals.rs:43:9
+ |
+LL | if x.is_some() {
+ | ----------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/simple_conditionals.rs:2:9
+ |
+LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `expect()` will always panic
+ --> $DIR/simple_conditionals.rs:44:9
+ |
+LL | if x.is_some() {
+ | ----------- because of this check
+...
+LL | x.expect("an error message"); // will panic
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/simple_conditionals.rs:47:9
+ |
+LL | if x.is_none() {
+ | ----------- because of this check
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap` on `x` after checking its variant with `is_none`
+ --> $DIR/simple_conditionals.rs:49:9
+ |
+LL | if x.is_none() {
+ | -------------- help: try: `if let Some(..) = x`
+...
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: called `unwrap` on `x` after checking its variant with `is_some`
+ --> $DIR/simple_conditionals.rs:8:13
+ |
+LL | if $a.is_some() {
+ | --------------- help: try: `if let Some(..) = x`
+LL | $a.unwrap(); // unnecessary
+ | ^^^^^^^^^^^
+...
+LL | m!(x);
+ | ----- in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: called `unwrap` on `x` after checking its variant with `is_ok`
+ --> $DIR/simple_conditionals.rs:57:9
+ |
+LL | if x.is_ok() {
+ | ------------ help: try: `if let Ok(..) = x`
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: called `expect` on `x` after checking its variant with `is_ok`
+ --> $DIR/simple_conditionals.rs:58:9
+ |
+LL | if x.is_ok() {
+ | ------------ help: try: `if let Ok(..) = x`
+LL | x.unwrap(); // unnecessary
+LL | x.expect("an error message"); // unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/simple_conditionals.rs:59:9
+ |
+LL | if x.is_ok() {
+ | --------- because of this check
+...
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/simple_conditionals.rs:61:9
+ |
+LL | if x.is_ok() {
+ | --------- because of this check
+...
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: this call to `expect()` will always panic
+ --> $DIR/simple_conditionals.rs:62:9
+ |
+LL | if x.is_ok() {
+ | --------- because of this check
+...
+LL | x.expect("an error message"); // will panic
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
+ --> $DIR/simple_conditionals.rs:63:9
+ |
+LL | if x.is_ok() {
+ | ------------ help: try: `if let Err(..) = x`
+...
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+ --> $DIR/simple_conditionals.rs:66:9
+ |
+LL | if x.is_err() {
+ | ---------- because of this check
+LL | x.unwrap(); // will panic
+ | ^^^^^^^^^^
+
+error: called `unwrap_err` on `x` after checking its variant with `is_err`
+ --> $DIR/simple_conditionals.rs:67:9
+ |
+LL | if x.is_err() {
+ | ------------- help: try: `if let Err(..) = x`
+LL | x.unwrap(); // will panic
+LL | x.unwrap_err(); // unnecessary
+ | ^^^^^^^^^^^^^^
+
+error: called `unwrap` on `x` after checking its variant with `is_err`
+ --> $DIR/simple_conditionals.rs:69:9
+ |
+LL | if x.is_err() {
+ | ------------- help: try: `if let Ok(..) = x`
+...
+LL | x.unwrap(); // unnecessary
+ | ^^^^^^^^^^
+
+error: this call to `unwrap_err()` will always panic
+ --> $DIR/simple_conditionals.rs:70:9
+ |
+LL | if x.is_err() {
+ | ---------- because of this check
+...
+LL | x.unwrap_err(); // will panic
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.fixed b/src/tools/clippy/tests/ui/clone_on_copy.fixed
new file mode 100644
index 000000000..dc0627626
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.fixed
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::redundant_clone,
+ clippy::deref_addrof,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::vec_init_then_push,
+ clippy::toplevel_ref_arg,
+ clippy::needless_borrow
+)]
+
+use std::cell::RefCell;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+fn is_ascii(ch: char) -> bool {
+ ch.is_ascii()
+}
+
+fn clone_on_copy() {
+ 42;
+
+ vec![1].clone(); // ok, not a Copy type
+ Some(vec![1]).clone(); // ok, not a Copy type
+ *(&42);
+
+ let rc = RefCell::new(0);
+ *rc.borrow();
+
+ let x = 0u32;
+ x.rotate_left(1);
+
+ #[derive(Clone, Copy)]
+ struct Foo;
+ impl Foo {
+ fn clone(&self) -> u32 {
+ 0
+ }
+ }
+ Foo.clone(); // ok, this is not the clone trait
+
+ macro_rules! m {
+ ($e:expr) => {{ $e }};
+ }
+ m!(42);
+
+ struct Wrap([u32; 2]);
+ impl core::ops::Deref for Wrap {
+ type Target = [u32; 2];
+ fn deref(&self) -> &[u32; 2] {
+ &self.0
+ }
+ }
+ let x = Wrap([0, 0]);
+ (*x)[0];
+
+ let x = 42;
+ let ref y = x.clone(); // ok, binds by reference
+ let ref mut y = x.clone(); // ok, binds by reference
+
+ // Issue #4348
+ let mut x = 43;
+ let _ = &x.clone(); // ok, getting a ref
+ 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
+ is_ascii('z');
+
+ // Issue #5436
+ let mut vec = Vec::new();
+ vec.push(42);
+}
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.rs b/src/tools/clippy/tests/ui/clone_on_copy.rs
new file mode 100644
index 000000000..8c39d0d55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.rs
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::redundant_clone,
+ clippy::deref_addrof,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::vec_init_then_push,
+ clippy::toplevel_ref_arg,
+ clippy::needless_borrow
+)]
+
+use std::cell::RefCell;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+fn is_ascii(ch: char) -> bool {
+ ch.is_ascii()
+}
+
+fn clone_on_copy() {
+ 42.clone();
+
+ vec![1].clone(); // ok, not a Copy type
+ Some(vec![1]).clone(); // ok, not a Copy type
+ (&42).clone();
+
+ let rc = RefCell::new(0);
+ rc.borrow().clone();
+
+ let x = 0u32;
+ x.clone().rotate_left(1);
+
+ #[derive(Clone, Copy)]
+ struct Foo;
+ impl Foo {
+ fn clone(&self) -> u32 {
+ 0
+ }
+ }
+ Foo.clone(); // ok, this is not the clone trait
+
+ macro_rules! m {
+ ($e:expr) => {{ $e }};
+ }
+ m!(42).clone();
+
+ struct Wrap([u32; 2]);
+ impl core::ops::Deref for Wrap {
+ type Target = [u32; 2];
+ fn deref(&self) -> &[u32; 2] {
+ &self.0
+ }
+ }
+ let x = Wrap([0, 0]);
+ x.clone()[0];
+
+ let x = 42;
+ let ref y = x.clone(); // ok, binds by reference
+ let ref mut y = x.clone(); // ok, binds by reference
+
+ // Issue #4348
+ let mut x = 43;
+ let _ = &x.clone(); // ok, getting a ref
+ 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
+ is_ascii('z'.clone());
+
+ // Issue #5436
+ let mut vec = Vec::new();
+ vec.push(42.clone());
+}
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr
new file mode 100644
index 000000000..861543d0a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr
@@ -0,0 +1,52 @@
+error: using `clone` on type `i32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:25:5
+ |
+LL | 42.clone();
+ | ^^^^^^^^^^ help: try removing the `clone` call: `42`
+ |
+ = note: `-D clippy::clone-on-copy` implied by `-D warnings`
+
+error: using `clone` on type `i32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:29:5
+ |
+LL | (&42).clone();
+ | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
+
+error: using `clone` on type `i32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:32:5
+ |
+LL | rc.borrow().clone();
+ | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
+
+error: using `clone` on type `u32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:35:5
+ |
+LL | x.clone().rotate_left(1);
+ | ^^^^^^^^^ help: try removing the `clone` call: `x`
+
+error: using `clone` on type `i32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:49:5
+ |
+LL | m!(42).clone();
+ | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)`
+
+error: using `clone` on type `[u32; 2]` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:59:5
+ |
+LL | x.clone()[0];
+ | ^^^^^^^^^ help: try dereferencing it: `(*x)`
+
+error: using `clone` on type `char` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:69:14
+ |
+LL | is_ascii('z'.clone());
+ | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
+
+error: using `clone` on type `i32` which implements the `Copy` trait
+ --> $DIR/clone_on_copy.rs:73:14
+ |
+LL | vec.push(42.clone());
+ | ^^^^^^^^^^ help: try removing the `clone` call: `42`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
new file mode 100644
index 000000000..8f9f2a0db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
@@ -0,0 +1,22 @@
+use std::fmt;
+use std::marker::PhantomData;
+
+pub struct Key<T> {
+ #[doc(hidden)]
+ pub __name: &'static str,
+ #[doc(hidden)]
+ pub __phantom: PhantomData<T>,
+}
+
+impl<T> Copy for Key<T> {}
+
+impl<T> Clone for Key<T> {
+ fn clone(&self) -> Self {
+ Key {
+ __name: self.__name,
+ __phantom: self.__phantom,
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
new file mode 100644
index 000000000..4eb999e18
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+ // yay
+ let _ = [1].iter().copied();
+ let _ = vec!["hi"].iter().copied();
+ let _ = Some(&1).copied();
+ let _ = Box::new([1].iter()).copied();
+ let _ = Box::new(Some(&1)).copied();
+
+ // nay
+ let _ = [String::new()].iter().cloned();
+ let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
new file mode 100644
index 000000000..894496c0e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+ // yay
+ let _ = [1].iter().cloned();
+ let _ = vec!["hi"].iter().cloned();
+ let _ = Some(&1).cloned();
+ let _ = Box::new([1].iter()).cloned();
+ let _ = Box::new(Some(&1)).cloned();
+
+ // nay
+ let _ = [String::new()].iter().cloned();
+ let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
new file mode 100644
index 000000000..e0707d321
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
@@ -0,0 +1,34 @@
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:6:24
+ |
+LL | let _ = [1].iter().cloned();
+ | ^^^^^^ help: try: `copied`
+ |
+ = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:7:31
+ |
+LL | let _ = vec!["hi"].iter().cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:8:22
+ |
+LL | let _ = Some(&1).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:9:34
+ |
+LL | let _ = Box::new([1].iter()).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+ --> $DIR/cloned_instead_of_copied.rs:10:32
+ |
+LL | let _ = Box::new(Some(&1)).cloned();
+ | ^^^^^^ help: try: `copied`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs
new file mode 100644
index 000000000..64ca52b01
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_nan.rs
@@ -0,0 +1,34 @@
+const NAN_F32: f32 = f32::NAN;
+const NAN_F64: f64 = f64::NAN;
+
+#[warn(clippy::cmp_nan)]
+#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)]
+fn main() {
+ let x = 5f32;
+ x == f32::NAN;
+ x != f32::NAN;
+ x < f32::NAN;
+ x > f32::NAN;
+ x <= f32::NAN;
+ x >= f32::NAN;
+ x == NAN_F32;
+ x != NAN_F32;
+ x < NAN_F32;
+ x > NAN_F32;
+ x <= NAN_F32;
+ x >= NAN_F32;
+
+ let y = 0f64;
+ y == f64::NAN;
+ y != f64::NAN;
+ y < f64::NAN;
+ y > f64::NAN;
+ y <= f64::NAN;
+ y >= f64::NAN;
+ y == NAN_F64;
+ y != NAN_F64;
+ y < NAN_F64;
+ y > NAN_F64;
+ y <= NAN_F64;
+ y >= NAN_F64;
+}
diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr
new file mode 100644
index 000000000..867516661
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_nan.stderr
@@ -0,0 +1,148 @@
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:8:5
+ |
+LL | x == f32::NAN;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cmp-nan` implied by `-D warnings`
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:9:5
+ |
+LL | x != f32::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:10:5
+ |
+LL | x < f32::NAN;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:11:5
+ |
+LL | x > f32::NAN;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:12:5
+ |
+LL | x <= f32::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:13:5
+ |
+LL | x >= f32::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:14:5
+ |
+LL | x == NAN_F32;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:15:5
+ |
+LL | x != NAN_F32;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:16:5
+ |
+LL | x < NAN_F32;
+ | ^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:17:5
+ |
+LL | x > NAN_F32;
+ | ^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:18:5
+ |
+LL | x <= NAN_F32;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:19:5
+ |
+LL | x >= NAN_F32;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:22:5
+ |
+LL | y == f64::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:23:5
+ |
+LL | y != f64::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:24:5
+ |
+LL | y < f64::NAN;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:25:5
+ |
+LL | y > f64::NAN;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:26:5
+ |
+LL | y <= f64::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:27:5
+ |
+LL | y >= f64::NAN;
+ | ^^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:28:5
+ |
+LL | y == NAN_F64;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:29:5
+ |
+LL | y != NAN_F64;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:30:5
+ |
+LL | y < NAN_F64;
+ | ^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:31:5
+ |
+LL | y > NAN_F64;
+ | ^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:32:5
+ |
+LL | y <= NAN_F64;
+ | ^^^^^^^^^^^^
+
+error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
+ --> $DIR/cmp_nan.rs:33:5
+ |
+LL | y >= NAN_F64;
+ | ^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs
new file mode 100644
index 000000000..2d2d04178
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_null.rs
@@ -0,0 +1,17 @@
+#![warn(clippy::cmp_null)]
+#![allow(unused_mut)]
+
+use std::ptr;
+
+fn main() {
+ let x = 0;
+ let p: *const usize = &x;
+ if p == ptr::null() {
+ println!("This is surprising!");
+ }
+ let mut y = 0;
+ let mut m: *mut usize = &mut y;
+ if m == ptr::null_mut() {
+ println!("This is surprising, too!");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr
new file mode 100644
index 000000000..a1f4c70fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_null.stderr
@@ -0,0 +1,16 @@
+error: comparing with null is better expressed by the `.is_null()` method
+ --> $DIR/cmp_null.rs:9:8
+ |
+LL | if p == ptr::null() {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cmp-null` implied by `-D warnings`
+
+error: comparing with null is better expressed by the `.is_null()` method
+ --> $DIR/cmp_null.rs:14:8
+ |
+LL | if m == ptr::null_mut() {
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
new file mode 100644
index 000000000..abd059c23
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
@@ -0,0 +1,93 @@
+// run-rustfix
+#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700
+
+// Define the types in each module to avoid trait impls leaking between modules.
+macro_rules! impl_types {
+ () => {
+ #[derive(PartialEq)]
+ pub struct Owned;
+
+ pub struct Borrowed;
+
+ impl ToOwned for Borrowed {
+ type Owned = Owned;
+ fn to_owned(&self) -> Owned {
+ Owned {}
+ }
+ }
+
+ impl std::borrow::Borrow<Borrowed> for Owned {
+ fn borrow(&self) -> &Borrowed {
+ static VALUE: Borrowed = Borrowed {};
+ &VALUE
+ }
+ }
+ };
+}
+
+// Only Borrowed == Owned is implemented
+mod borrowed_eq_owned {
+ impl_types!();
+
+ impl PartialEq<Owned> for Borrowed {
+ fn eq(&self, _: &Owned) -> bool {
+ true
+ }
+ }
+
+ pub fn compare() {
+ let owned = Owned {};
+ let borrowed = Borrowed {};
+
+ if borrowed == owned {}
+ if borrowed == owned {}
+ }
+}
+
+// Only Owned == Borrowed is implemented
+mod owned_eq_borrowed {
+ impl_types!();
+
+ impl PartialEq<Borrowed> for Owned {
+ fn eq(&self, _: &Borrowed) -> bool {
+ true
+ }
+ }
+
+ fn compare() {
+ let owned = Owned {};
+ let borrowed = Borrowed {};
+
+ if owned == borrowed {}
+ if owned == borrowed {}
+ }
+}
+
+mod issue_4874 {
+ impl_types!();
+
+ // NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
+ impl<T> PartialEq<T> for Borrowed
+ where
+ T: AsRef<str> + ?Sized,
+ {
+ fn eq(&self, _: &T) -> bool {
+ true
+ }
+ }
+
+ impl std::fmt::Display for Borrowed {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "borrowed")
+ }
+ }
+
+ fn compare() {
+ let borrowed = Borrowed {};
+
+ if borrowed == "Hi" {}
+ if borrowed == "Hi" {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
new file mode 100644
index 000000000..020ef5f84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
@@ -0,0 +1,93 @@
+// run-rustfix
+#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700
+
+// Define the types in each module to avoid trait impls leaking between modules.
+macro_rules! impl_types {
+ () => {
+ #[derive(PartialEq)]
+ pub struct Owned;
+
+ pub struct Borrowed;
+
+ impl ToOwned for Borrowed {
+ type Owned = Owned;
+ fn to_owned(&self) -> Owned {
+ Owned {}
+ }
+ }
+
+ impl std::borrow::Borrow<Borrowed> for Owned {
+ fn borrow(&self) -> &Borrowed {
+ static VALUE: Borrowed = Borrowed {};
+ &VALUE
+ }
+ }
+ };
+}
+
+// Only Borrowed == Owned is implemented
+mod borrowed_eq_owned {
+ impl_types!();
+
+ impl PartialEq<Owned> for Borrowed {
+ fn eq(&self, _: &Owned) -> bool {
+ true
+ }
+ }
+
+ pub fn compare() {
+ let owned = Owned {};
+ let borrowed = Borrowed {};
+
+ if borrowed.to_owned() == owned {}
+ if owned == borrowed.to_owned() {}
+ }
+}
+
+// Only Owned == Borrowed is implemented
+mod owned_eq_borrowed {
+ impl_types!();
+
+ impl PartialEq<Borrowed> for Owned {
+ fn eq(&self, _: &Borrowed) -> bool {
+ true
+ }
+ }
+
+ fn compare() {
+ let owned = Owned {};
+ let borrowed = Borrowed {};
+
+ if owned == borrowed.to_owned() {}
+ if borrowed.to_owned() == owned {}
+ }
+}
+
+mod issue_4874 {
+ impl_types!();
+
+ // NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
+ impl<T> PartialEq<T> for Borrowed
+ where
+ T: AsRef<str> + ?Sized,
+ {
+ fn eq(&self, _: &T) -> bool {
+ true
+ }
+ }
+
+ impl std::fmt::Display for Borrowed {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "borrowed")
+ }
+ }
+
+ fn compare() {
+ let borrowed = Borrowed {};
+
+ if "Hi" == borrowed.to_string() {}
+ if borrowed.to_string() == "Hi" {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
new file mode 100644
index 000000000..43bf8851f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
@@ -0,0 +1,46 @@
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:42:12
+ |
+LL | if borrowed.to_owned() == owned {}
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+ |
+ = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:43:21
+ |
+LL | if owned == borrowed.to_owned() {}
+ | ---------^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try: `borrowed == owned`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:61:21
+ |
+LL | if owned == borrowed.to_owned() {}
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:62:12
+ |
+LL | if borrowed.to_owned() == owned {}
+ | ^^^^^^^^^^^^^^^^^^^---------
+ | |
+ | help: try: `owned == borrowed`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:88:20
+ |
+LL | if "Hi" == borrowed.to_string() {}
+ | --------^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try: `borrowed == "Hi"`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/asymmetric_partial_eq.rs:89:12
+ |
+LL | if borrowed.to_string() == "Hi" {}
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.fixed b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.fixed
new file mode 100644
index 000000000..44e41bdd1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+
+use std::fmt::{self, Display};
+
+fn main() {
+ let a = Foo;
+
+ if a != "bar" {
+ println!("foo");
+ }
+
+ if a != "bar" {
+ println!("foo");
+ }
+}
+
+struct Foo;
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "foo")
+ }
+}
+
+impl PartialEq<&str> for Foo {
+ fn eq(&self, other: &&str) -> bool {
+ "foo" == *other
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.rs b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.rs
new file mode 100644
index 000000000..662673abb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+
+use std::fmt::{self, Display};
+
+fn main() {
+ let a = Foo;
+
+ if a.to_string() != "bar" {
+ println!("foo");
+ }
+
+ if "bar" != a.to_string() {
+ println!("foo");
+ }
+}
+
+struct Foo;
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "foo")
+ }
+}
+
+impl PartialEq<&str> for Foo {
+ fn eq(&self, other: &&str) -> bool {
+ "foo" == *other
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.stderr b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.stderr
new file mode 100644
index 000000000..e4d0d822b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/comparison_flip.stderr
@@ -0,0 +1,18 @@
+error: this creates an owned instance just for comparison
+ --> $DIR/comparison_flip.rs:8:8
+ |
+LL | if a.to_string() != "bar" {
+ | ^^^^^^^^^^^^^ help: try: `a`
+ |
+ = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/comparison_flip.rs:12:17
+ |
+LL | if "bar" != a.to_string() {
+ | ---------^^^^^^^^^^^^^
+ | |
+ | help: try: `a != "bar"`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
new file mode 100644
index 000000000..b28c4378e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
@@ -0,0 +1,72 @@
+// run-rustfix
+
+#[warn(clippy::cmp_owned)]
+#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
+fn main() {
+ fn with_to_string(x: &str) {
+ x != "foo";
+
+ "foo" != x;
+ }
+
+ let x = "oh";
+
+ with_to_string(x);
+
+ x != "foo";
+
+ x != "foo";
+
+ 42.to_string() == "42";
+
+ Foo == Foo;
+
+ "abc".chars().filter(|c| *c != 'X');
+
+ "abc".chars().filter(|c| *c != 'X');
+}
+
+struct Foo;
+
+impl PartialEq for Foo {
+ // Allow this here, because it emits the lint
+ // without a suggestion. This is tested in
+ // `tests/ui/cmp_owned/without_suggestion.rs`
+ #[allow(clippy::cmp_owned)]
+ fn eq(&self, other: &Self) -> bool {
+ self.to_owned() == *other
+ }
+}
+
+impl ToOwned for Foo {
+ type Owned = Bar;
+ fn to_owned(&self) -> Bar {
+ Bar
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Bar;
+
+impl PartialEq<Foo> for Bar {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+}
+
+impl std::borrow::Borrow<Foo> for Bar {
+ fn borrow(&self) -> &Foo {
+ static FOO: Foo = Foo;
+ &FOO
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Baz;
+
+impl ToOwned for Baz {
+ type Owned = Baz;
+ fn to_owned(&self) -> Baz {
+ Baz
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs
new file mode 100644
index 000000000..c1089010f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs
@@ -0,0 +1,72 @@
+// run-rustfix
+
+#[warn(clippy::cmp_owned)]
+#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
+fn main() {
+ fn with_to_string(x: &str) {
+ x != "foo".to_string();
+
+ "foo".to_string() != x;
+ }
+
+ let x = "oh";
+
+ with_to_string(x);
+
+ x != "foo".to_owned();
+
+ x != String::from("foo");
+
+ 42.to_string() == "42";
+
+ Foo.to_owned() == Foo;
+
+ "abc".chars().filter(|c| c.to_owned() != 'X');
+
+ "abc".chars().filter(|c| *c != 'X');
+}
+
+struct Foo;
+
+impl PartialEq for Foo {
+ // Allow this here, because it emits the lint
+ // without a suggestion. This is tested in
+ // `tests/ui/cmp_owned/without_suggestion.rs`
+ #[allow(clippy::cmp_owned)]
+ fn eq(&self, other: &Self) -> bool {
+ self.to_owned() == *other
+ }
+}
+
+impl ToOwned for Foo {
+ type Owned = Bar;
+ fn to_owned(&self) -> Bar {
+ Bar
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Bar;
+
+impl PartialEq<Foo> for Bar {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+}
+
+impl std::borrow::Borrow<Foo> for Bar {
+ fn borrow(&self) -> &Foo {
+ static FOO: Foo = Foo;
+ &FOO
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Baz;
+
+impl ToOwned for Baz {
+ type Owned = Baz;
+ fn to_owned(&self) -> Baz {
+ Baz
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr
new file mode 100644
index 000000000..2f333e6ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr
@@ -0,0 +1,40 @@
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:7:14
+ |
+LL | x != "foo".to_string();
+ | ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
+ |
+ = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:9:9
+ |
+LL | "foo".to_string() != x;
+ | ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:16:10
+ |
+LL | x != "foo".to_owned();
+ | ^^^^^^^^^^^^^^^^ help: try: `"foo"`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:18:10
+ |
+LL | x != String::from("foo");
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `"foo"`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:22:5
+ |
+LL | Foo.to_owned() == Foo;
+ | ^^^^^^^^^^^^^^ help: try: `Foo`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/with_suggestion.rs:24:30
+ |
+LL | "abc".chars().filter(|c| c.to_owned() != 'X');
+ | ^^^^^^^^^^^^ help: try: `*c`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
new file mode 100644
index 000000000..d8a202cb6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
@@ -0,0 +1,75 @@
+#[allow(clippy::unnecessary_operation)]
+#[allow(clippy::implicit_clone)]
+
+fn main() {
+ let x = &Baz;
+ let y = &Baz;
+ y.to_owned() == *x;
+
+ let x = &&Baz;
+ let y = &Baz;
+ y.to_owned() == **x;
+
+ let x = 0u32;
+ let y = U32Wrapper(x);
+ let _ = U32Wrapper::from(x) == y;
+}
+
+struct Foo;
+
+impl PartialEq for Foo {
+ fn eq(&self, other: &Self) -> bool {
+ self.to_owned() == *other
+ }
+}
+
+impl ToOwned for Foo {
+ type Owned = Bar;
+ fn to_owned(&self) -> Bar {
+ Bar
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Baz;
+
+impl ToOwned for Baz {
+ type Owned = Baz;
+ fn to_owned(&self) -> Baz {
+ Baz
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Bar;
+
+impl PartialEq<Foo> for Bar {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+}
+
+impl std::borrow::Borrow<Foo> for Bar {
+ fn borrow(&self) -> &Foo {
+ static FOO: Foo = Foo;
+ &FOO
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct U32Wrapper(u32);
+impl From<u32> for U32Wrapper {
+ fn from(x: u32) -> Self {
+ Self(x)
+ }
+}
+impl PartialEq<u32> for U32Wrapper {
+ fn eq(&self, other: &u32) -> bool {
+ self.0 == *other
+ }
+}
+impl PartialEq<U32Wrapper> for u32 {
+ fn eq(&self, other: &U32Wrapper) -> bool {
+ *self == other.0
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr
new file mode 100644
index 000000000..d2dd14d8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr
@@ -0,0 +1,22 @@
+error: this creates an owned instance just for comparison
+ --> $DIR/without_suggestion.rs:7:5
+ |
+LL | y.to_owned() == *x;
+ | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
+ |
+ = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/without_suggestion.rs:11:5
+ |
+LL | y.to_owned() == **x;
+ | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
+
+error: this creates an owned instance just for comparison
+ --> $DIR/without_suggestion.rs:22:9
+ |
+LL | self.to_owned() == *other
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.rs b/src/tools/clippy/tests/ui/cognitive_complexity.rs
new file mode 100644
index 000000000..912e6788a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cognitive_complexity.rs
@@ -0,0 +1,395 @@
+#![allow(clippy::all)]
+#![warn(clippy::cognitive_complexity)]
+#![allow(unused, unused_crate_dependencies)]
+
+#[rustfmt::skip]
+fn main() {
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+ if true {
+ println!("a");
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn kaboom() {
+ let n = 0;
+ 'a: for i in 0..20 {
+ 'b: for j in i..20 {
+ for k in j..20 {
+ if k == 5 {
+ break 'b;
+ }
+ if j == 3 && k == 6 {
+ continue 'a;
+ }
+ if k == j {
+ continue;
+ }
+ println!("bake");
+ }
+ }
+ println!("cake");
+ }
+}
+
+fn bloo() {
+ match 42 {
+ 0 => println!("hi"),
+ 1 => println!("hai"),
+ 2 => println!("hey"),
+ 3 => println!("hallo"),
+ 4 => println!("hello"),
+ 5 => println!("salut"),
+ 6 => println!("good morning"),
+ 7 => println!("good evening"),
+ 8 => println!("good afternoon"),
+ 9 => println!("good night"),
+ 10 => println!("bonjour"),
+ 11 => println!("hej"),
+ 12 => println!("hej hej"),
+ 13 => println!("greetings earthling"),
+ 14 => println!("take us to you leader"),
+ 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"),
+ 35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"),
+ 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"),
+ _ => println!("bye"),
+ }
+}
+
+// Short circuiting operations don't increase the complexity of a function.
+// Note that the minimum complexity of a function is 1.
+#[clippy::cognitive_complexity = "1"]
+fn lots_of_short_circuits() -> bool {
+ true && false && true && false && true && false && true
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn lots_of_short_circuits2() -> bool {
+ true || false || true || false || true || false || true
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn baa() {
+ let x = || match 99 {
+ 0 => 0,
+ 1 => 1,
+ 2 => 2,
+ 4 => 4,
+ 6 => 6,
+ 9 => 9,
+ _ => 42,
+ };
+ if x() == 42 {
+ println!("x");
+ } else {
+ println!("not x");
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn bar() {
+ match 99 {
+ 0 => println!("hi"),
+ _ => println!("bye"),
+ }
+}
+
+#[test]
+#[clippy::cognitive_complexity = "1"]
+/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to
+/// give lots of false-positives in tests.
+fn dont_warn_on_tests() {
+ match 99 {
+ 0 => println!("hi"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barr() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barr2() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barrr() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => panic!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barrr2() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => panic!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+ match 99 {
+ 0 => println!("hi"),
+ 1 => panic!("bla"),
+ 2 | 3 => println!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barrrr() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => panic!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn barrrr2() {
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => panic!("blub"),
+ _ => println!("bye"),
+ }
+ match 99 {
+ 0 => println!("hi"),
+ 1 => println!("bla"),
+ 2 | 3 => panic!("blub"),
+ _ => println!("bye"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn cake() {
+ if 4 == 5 {
+ println!("yea");
+ } else {
+ panic!("meh");
+ }
+ println!("whee");
+}
+
+#[clippy::cognitive_complexity = "1"]
+pub fn read_file(input_path: &str) -> String {
+ use std::fs::File;
+ use std::io::{Read, Write};
+ use std::path::Path;
+ let mut file = match File::open(&Path::new(input_path)) {
+ Ok(f) => f,
+ Err(err) => {
+ panic!("Can't open {}: {}", input_path, err);
+ },
+ };
+
+ let mut bytes = Vec::new();
+
+ match file.read_to_end(&mut bytes) {
+ Ok(..) => {},
+ Err(_) => {
+ panic!("Can't read {}", input_path);
+ },
+ };
+
+ match String::from_utf8(bytes) {
+ Ok(contents) => contents,
+ Err(_) => {
+ panic!("{} is not UTF-8 encoded", input_path);
+ },
+ }
+}
+
+enum Void {}
+
+#[clippy::cognitive_complexity = "1"]
+fn void(void: Void) {
+ if true {
+ match void {}
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn mcarton_sees_all() {
+ panic!("meh");
+ panic!("möh");
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn try_() -> Result<i32, &'static str> {
+ match 5 {
+ 5 => Ok(5),
+ _ => return Err("bla"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn try_again() -> Result<i32, &'static str> {
+ let _ = Ok(42)?;
+ let _ = Ok(43)?;
+ let _ = Ok(44)?;
+ let _ = Ok(45)?;
+ let _ = Ok(46)?;
+ let _ = Ok(47)?;
+ let _ = Ok(48)?;
+ let _ = Ok(49)?;
+ match 5 {
+ 5 => Ok(5),
+ _ => return Err("bla"),
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn early() -> Result<i32, &'static str> {
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+ return Ok(5);
+}
+
+#[rustfmt::skip]
+#[clippy::cognitive_complexity = "1"]
+fn early_ret() -> i32 {
+ let a = if true { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ let a = if a < 99 { 42 } else { return 0; };
+ match 5 {
+ 5 => 5,
+ _ => return 6,
+ }
+}
+
+#[clippy::cognitive_complexity = "1"]
+fn closures() {
+ let x = |a: i32, b: i32| -> i32 {
+ if true {
+ println!("moo");
+ }
+
+ a + b
+ };
+}
+
+struct Moo;
+
+#[clippy::cognitive_complexity = "1"]
+impl Moo {
+ fn moo(&self) {
+ if true {
+ println!("moo");
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr
new file mode 100644
index 000000000..a0ddc673a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr
@@ -0,0 +1,139 @@
+error: the function has a cognitive complexity of (28/25)
+ --> $DIR/cognitive_complexity.rs:6:4
+ |
+LL | fn main() {
+ | ^^^^
+ |
+ = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (7/1)
+ --> $DIR/cognitive_complexity.rs:91:4
+ |
+LL | fn kaboom() {
+ | ^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:149:4
+ |
+LL | fn baa() {
+ | ^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:150:13
+ |
+LL | let x = || match 99 {
+ | ^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:167:4
+ |
+LL | fn bar() {
+ | ^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:186:4
+ |
+LL | fn barr() {
+ | ^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (3/1)
+ --> $DIR/cognitive_complexity.rs:196:4
+ |
+LL | fn barr2() {
+ | ^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:212:4
+ |
+LL | fn barrr() {
+ | ^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (3/1)
+ --> $DIR/cognitive_complexity.rs:222:4
+ |
+LL | fn barrr2() {
+ | ^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:238:4
+ |
+LL | fn barrrr() {
+ | ^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (3/1)
+ --> $DIR/cognitive_complexity.rs:248:4
+ |
+LL | fn barrrr2() {
+ | ^^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:264:4
+ |
+LL | fn cake() {
+ | ^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (4/1)
+ --> $DIR/cognitive_complexity.rs:274:8
+ |
+LL | pub fn read_file(input_path: &str) -> String {
+ | ^^^^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:305:4
+ |
+LL | fn void(void: Void) {
+ | ^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (8/1)
+ --> $DIR/cognitive_complexity.rs:356:4
+ |
+LL | fn early_ret() -> i32 {
+ | ^^^^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:377:13
+ |
+LL | let x = |a: i32, b: i32| -> i32 {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: the function has a cognitive complexity of (2/1)
+ --> $DIR/cognitive_complexity.rs:390:8
+ |
+LL | fn moo(&self) {
+ | ^^^
+ |
+ = help: you could split it up into multiple smaller functions
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs
new file mode 100644
index 000000000..771a26fc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs
@@ -0,0 +1,15 @@
+#![warn(unused, clippy::cognitive_complexity)]
+#![allow(unused_crate_dependencies)]
+
+fn main() {
+ kaboom();
+}
+
+#[clippy::cognitive_complexity = "0"]
+fn kaboom() {
+ if 42 == 43 {
+ panic!();
+ } else if "cake" == "lie" {
+ println!("what?");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr
new file mode 100644
index 000000000..f5ff53dda
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr
@@ -0,0 +1,11 @@
+error: the function has a cognitive complexity of (3/0)
+ --> $DIR/cognitive_complexity_attr_used.rs:9:4
+ |
+LL | fn kaboom() {
+ | ^^^^^^
+ |
+ = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
+ = help: you could split it up into multiple smaller functions
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
new file mode 100644
index 000000000..d6a5a7850
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
@@ -0,0 +1,84 @@
+// run-rustfix
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+#[warn(clippy::collapsible_else_if)]
+
+fn main() {
+ let x = "hello";
+ let y = "world";
+ // Collapse `else { if .. }` to `else if ..`
+ if x == "hello" {
+ print!("Hello ");
+ } else if y == "world" {
+ println!("world!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if y == "world" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if x == "hello" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ #[cfg(not(roflol))]
+ if y == "world" {
+ println!("world!")
+ }
+ }
+}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+ if true { println!("I've been resolved!")
+ }else if false {}
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs
new file mode 100644
index 000000000..4399fc8b2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs
@@ -0,0 +1,100 @@
+// run-rustfix
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+#[warn(clippy::collapsible_else_if)]
+
+fn main() {
+ let x = "hello";
+ let y = "world";
+ // Collapse `else { if .. }` to `else if ..`
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if y == "world" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if x == "hello" {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if let Some(42) = Some(42) {
+ print!("Hello ");
+ } else {
+ if let Some(42) = Some(42) {
+ println!("world")
+ }
+ else {
+ println!("!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ #[cfg(not(roflol))]
+ if y == "world" {
+ println!("world!")
+ }
+ }
+}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+ if true { println!("I've been resolved!")
+ }else{
+ if false {}
+ }
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
new file mode 100644
index 000000000..45b2094c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
@@ -0,0 +1,163 @@
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:14:12
+ |
+LL | } else {
+ | ____________^
+LL | | if y == "world" {
+LL | | println!("world!")
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::collapsible-else-if` implied by `-D warnings`
+help: collapse nested if block
+ |
+LL ~ } else if y == "world" {
+LL + println!("world!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:22:12
+ |
+LL | } else {
+ | ____________^
+LL | | if let Some(42) = Some(42) {
+LL | | println!("world!")
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if let Some(42) = Some(42) {
+LL + println!("world!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:30:12
+ |
+LL | } else {
+ | ____________^
+LL | | if y == "world" {
+LL | | println!("world")
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if y == "world" {
+LL + println!("world")
+LL + }
+LL + else {
+LL + println!("!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:41:12
+ |
+LL | } else {
+ | ____________^
+LL | | if let Some(42) = Some(42) {
+LL | | println!("world")
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if let Some(42) = Some(42) {
+LL + println!("world")
+LL + }
+LL + else {
+LL + println!("!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:52:12
+ |
+LL | } else {
+ | ____________^
+LL | | if let Some(42) = Some(42) {
+LL | | println!("world")
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if let Some(42) = Some(42) {
+LL + println!("world")
+LL + }
+LL + else {
+LL + println!("!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:63:12
+ |
+LL | } else {
+ | ____________^
+LL | | if x == "hello" {
+LL | | println!("world")
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if x == "hello" {
+LL + println!("world")
+LL + }
+LL + else {
+LL + println!("!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:74:12
+ |
+LL | } else {
+ | ____________^
+LL | | if let Some(42) = Some(42) {
+LL | | println!("world")
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ } else if let Some(42) = Some(42) {
+LL + println!("world")
+LL + }
+LL + else {
+LL + println!("!")
+LL + }
+ |
+
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:97:10
+ |
+LL | }else{
+ | __________^
+LL | | if false {}
+LL | | }
+ | |_____^ help: collapse nested if block: `if false {}`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed
new file mode 100644
index 000000000..5b0e4a473
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_if.fixed
@@ -0,0 +1,148 @@
+// run-rustfix
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+fn main() {
+ let x = "hello";
+ let y = "world";
+ if x == "hello" && y == "world" {
+ println!("Hello world!");
+ }
+
+ if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
+ println!("Hello world!");
+ }
+
+ if x == "hello" && x == "world" && (y == "world" || y == "hello") {
+ println!("Hello world!");
+ }
+
+ if (x == "hello" || x == "world") && y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+
+ if x == "hello" && x == "world" && y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+
+ if 42 == 1337 && 'a' != 'A' {
+ println!("world!")
+ }
+
+ // Works because any if with an else statement cannot be collapsed.
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ } else {
+ println!("Not Hello world");
+ }
+
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ } else {
+ println!("Hello something else");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if true {
+ } else {
+ assert!(true); // assert! is just an `if`
+ }
+
+
+ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
+ if x == "hello" {// Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && y == "world" { // Collapsible
+ println!("Hello world!");
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ // Test behavior wrt. `let_chains`.
+ // None of the cases below should be collapsed.
+ fn truth() -> bool { true }
+
+ // Prefix:
+ if let 0 = 1 {
+ if truth() {}
+ }
+
+ // Suffix:
+ if truth() {
+ if let 0 = 1 {}
+ }
+
+ // Midfix:
+ if truth() {
+ if let 0 = 1 {
+ if truth() {}
+ }
+ }
+
+ // Fix #5962
+ if matches!(true, true) && matches!(true, true) {}
+
+ if true {
+ #[cfg(not(teehee))]
+ if true {
+ println!("Hello world!");
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs
new file mode 100644
index 000000000..cd231a5d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_if.rs
@@ -0,0 +1,164 @@
+// run-rustfix
+#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
+
+#[rustfmt::skip]
+#[warn(clippy::collapsible_if)]
+fn main() {
+ let x = "hello";
+ let y = "world";
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" || x == "world" {
+ if y == "world" || y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && x == "world" {
+ if y == "world" || y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" || x == "world" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" && x == "world" {
+ if y == "world" && y == "hello" {
+ println!("Hello world!");
+ }
+ }
+
+ if 42 == 1337 {
+ if 'a' != 'A' {
+ println!("world!")
+ }
+ }
+
+ // Works because any if with an else statement cannot be collapsed.
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ }
+ } else {
+ println!("Not Hello world");
+ }
+
+ if x == "hello" {
+ if y == "world" {
+ println!("Hello world!");
+ } else {
+ println!("Hello something else");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if true {
+ } else {
+ assert!(true); // assert! is just an `if`
+ }
+
+
+ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
+ if x == "hello" {// Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ // Not collapsible
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ if y == "world" { // Collapsible
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if y == "world" {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ print!("Hello ");
+ } else {
+ // Not collapsible
+ if let Some(42) = Some(42) {
+ println!("world!")
+ }
+ }
+
+ if x == "hello" {
+ /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ if x == "hello" { /* Not collapsible */
+ if y == "world" {
+ println!("Hello world!");
+ }
+ }
+
+ // Test behavior wrt. `let_chains`.
+ // None of the cases below should be collapsed.
+ fn truth() -> bool { true }
+
+ // Prefix:
+ if let 0 = 1 {
+ if truth() {}
+ }
+
+ // Suffix:
+ if truth() {
+ if let 0 = 1 {}
+ }
+
+ // Midfix:
+ if truth() {
+ if let 0 = 1 {
+ if truth() {}
+ }
+ }
+
+ // Fix #5962
+ if matches!(true, true) {
+ if matches!(true, true) {}
+ }
+
+ if true {
+ #[cfg(not(teehee))]
+ if true {
+ println!("Hello world!");
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr
new file mode 100644
index 000000000..674961238
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_if.stderr
@@ -0,0 +1,130 @@
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:9:5
+ |
+LL | / if x == "hello" {
+LL | | if y == "world" {
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::collapsible-if` implied by `-D warnings`
+help: collapse nested if block
+ |
+LL ~ if x == "hello" && y == "world" {
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:15:5
+ |
+LL | / if x == "hello" || x == "world" {
+LL | | if y == "world" || y == "hello" {
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:21:5
+ |
+LL | / if x == "hello" && x == "world" {
+LL | | if y == "world" || y == "hello" {
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if x == "hello" && x == "world" && (y == "world" || y == "hello") {
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:27:5
+ |
+LL | / if x == "hello" || x == "world" {
+LL | | if y == "world" && y == "hello" {
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if (x == "hello" || x == "world") && y == "world" && y == "hello" {
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:33:5
+ |
+LL | / if x == "hello" && x == "world" {
+LL | | if y == "world" && y == "hello" {
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if x == "hello" && x == "world" && y == "world" && y == "hello" {
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:39:5
+ |
+LL | / if 42 == 1337 {
+LL | | if 'a' != 'A' {
+LL | | println!("world!")
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if 42 == 1337 && 'a' != 'A' {
+LL + println!("world!")
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:95:5
+ |
+LL | / if x == "hello" {
+LL | | if y == "world" { // Collapsible
+LL | | println!("Hello world!");
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: collapse nested if block
+ |
+LL ~ if x == "hello" && y == "world" { // Collapsible
+LL + println!("Hello world!");
+LL + }
+ |
+
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:154:5
+ |
+LL | / if matches!(true, true) {
+LL | | if matches!(true, true) {}
+LL | | }
+ | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs
new file mode 100644
index 000000000..603ae7dc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_match.rs
@@ -0,0 +1,265 @@
+#![warn(clippy::collapsible_match)]
+#![allow(
+ clippy::needless_return,
+ clippy::no_effect,
+ clippy::single_match,
+ clippy::equatable_if_let
+)]
+
+fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
+ // match without block
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // match with block
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // if let, if let
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ take(n);
+ }
+ }
+
+ // if let else, if let else
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ take(n);
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ // if let, match
+ if let Ok(val) = res_opt {
+ match val {
+ Some(n) => foo(n),
+ _ => (),
+ }
+ }
+
+ // match, if let
+ match res_opt {
+ Ok(val) => {
+ if let Some(n) = val {
+ take(n);
+ }
+ },
+ _ => {},
+ }
+
+ // if let else, match
+ if let Ok(val) = res_opt {
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ } else {
+ return;
+ }
+
+ // match, if let else
+ match res_opt {
+ Ok(val) => {
+ if let Some(n) = val {
+ take(n);
+ } else {
+ return;
+ }
+ },
+ _ => return,
+ }
+
+ // None in inner match same as outer wild branch
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ None => return,
+ },
+ _ => return,
+ }
+
+ // None in outer match same as inner wild branch
+ match opt_opt {
+ Some(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ None => return,
+ }
+}
+
+fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u32, String>, String>) {
+ while let Some(x) = make() {
+ if let Some(1) = x {
+ todo!();
+ }
+ }
+ // no wild pattern in outer match
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ Err(_) => return,
+ }
+
+ // inner branch is not wild or None
+ match res_res {
+ Ok(val) => match val {
+ Ok(n) => foo(n),
+ Err(_) => return,
+ },
+ _ => return,
+ }
+
+ // statement before inner match
+ match res_opt {
+ Ok(val) => {
+ "hi buddy";
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ },
+ _ => return,
+ }
+
+ // statement after inner match
+ match res_opt {
+ Ok(val) => {
+ match val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ "hi buddy";
+ },
+ _ => return,
+ }
+
+ // wild branches do not match
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => {
+ "sup";
+ return;
+ },
+ },
+ _ => return,
+ }
+
+ // binding used in if guard
+ match res_opt {
+ Ok(val) if val.is_some() => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // binding used in inner match body
+ match res_opt {
+ Ok(val) => match val {
+ Some(_) => take(val),
+ _ => return,
+ },
+ _ => return,
+ }
+
+ // if guard on inner match
+ {
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) if make() => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+ match res_opt {
+ Ok(val) => match val {
+ _ => make(),
+ _ if make() => return,
+ },
+ _ => return,
+ }
+ }
+
+ // differing macro contexts
+ {
+ macro_rules! mac {
+ ($val:ident) => {
+ match $val {
+ Some(n) => foo(n),
+ _ => return,
+ }
+ };
+ }
+ match res_opt {
+ Ok(val) => mac!(val),
+ _ => return,
+ }
+ }
+
+ // OR pattern
+ enum E<T> {
+ A(T),
+ B(T),
+ C(T),
+ };
+ match make::<E<Option<u32>>>() {
+ E::A(val) | E::B(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+ match make::<Option<E<u32>>>() {
+ Some(val) => match val {
+ E::A(val) | E::B(val) => foo(val),
+ _ => return,
+ },
+ _ => return,
+ }
+ if let Ok(val) = res_opt {
+ if let Some(n) = val {
+ let _ = || {
+ // usage in closure
+ println!("{:?}", val);
+ };
+ }
+ }
+ let _: &dyn std::any::Any = match &Some(Some(1)) {
+ Some(e) => match e {
+ Some(e) => e,
+ e => e,
+ },
+ // else branch looks the same but the binding is different
+ e => e,
+ };
+}
+
+fn make<T>() -> T {
+ unimplemented!()
+}
+
+fn foo<T, U>(t: T) -> U {
+ unimplemented!()
+}
+
+fn take<T>(t: T) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr
new file mode 100644
index 000000000..5f18b6935
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_match.stderr
@@ -0,0 +1,179 @@
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:12:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+ = note: `-D clippy::collapsible-match` implied by `-D warnings`
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:12:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:21:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:21:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `if let`
+ --> $DIR/collapsible_match.rs:30:9
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:29:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `if let`
+ --> $DIR/collapsible_match.rs:37:9
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | } else {
+LL | | return;
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:36:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
+ --> $DIR/collapsible_match.rs:48:9
+ |
+LL | / match val {
+LL | | Some(n) => foo(n),
+LL | | _ => (),
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:47:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | match val {
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:57:13
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | }
+ | |_____________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:56:12
+ |
+LL | Ok(val) => {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
+ --> $DIR/collapsible_match.rs:66:9
+ |
+LL | / match val {
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | }
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:65:15
+ |
+LL | if let Ok(val) = res_opt {
+ | ^^^ replace this binding
+LL | match val {
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `if let` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:77:13
+ |
+LL | / if let Some(n) = val {
+LL | | take(n);
+LL | | } else {
+LL | | return;
+LL | | }
+ | |_____________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:76:12
+ |
+LL | Ok(val) => {
+ | ^^^ replace this binding
+LL | if let Some(n) = val {
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:88:20
+ |
+LL | Ok(val) => match val {
+ | ____________________^
+LL | | Some(n) => foo(n),
+LL | | None => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:88:12
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match.rs:97:22
+ |
+LL | Some(val) => match val {
+ | ______________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match.rs:97:14
+ |
+LL | Some(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collapsible_match2.rs b/src/tools/clippy/tests/ui/collapsible_match2.rs
new file mode 100644
index 000000000..c8fb0a39e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_match2.rs
@@ -0,0 +1,87 @@
+#![warn(clippy::collapsible_match)]
+#![allow(
+ clippy::needless_return,
+ clippy::no_effect,
+ clippy::single_match,
+ clippy::needless_borrow
+)]
+
+fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
+ // if guards on outer match
+ {
+ match res_opt {
+ Ok(val) if make() => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ => return,
+ }
+ match res_opt {
+ Ok(val) => match val {
+ Some(n) => foo(n),
+ _ => return,
+ },
+ _ if make() => return,
+ _ => return,
+ }
+ }
+
+ // macro
+ {
+ macro_rules! mac {
+ ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => {
+ match $outer {
+ $pat => match $e {
+ $inner_pat => $then,
+ _ => return,
+ },
+ _ => return,
+ }
+ };
+ }
+ // Lint this since the patterns are not defined by the macro.
+ // Allows the lint to work on if_chain! for example.
+ // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that
+ // there is still a better way to write this.
+ mac!(res_opt => Ok(val), val => Some(n), foo(n));
+ }
+
+ // deref reference value
+ match Some(&[1]) {
+ Some(s) => match *s {
+ [n] => foo(n),
+ _ => (),
+ },
+ _ => (),
+ }
+
+ // ref pattern and deref
+ match Some(&[1]) {
+ Some(ref s) => match s {
+ [n] => foo(n),
+ _ => (),
+ },
+ _ => (),
+ }
+}
+
+fn no_lint() {
+ // deref inner value (cannot pattern match with Vec)
+ match Some(vec![1]) {
+ Some(s) => match *s {
+ [n] => foo(n),
+ _ => (),
+ },
+ _ => (),
+ }
+}
+
+fn make<T>() -> T {
+ unimplemented!()
+}
+
+fn foo<T, U>(t: T) -> U {
+ unimplemented!()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/collapsible_match2.stderr b/src/tools/clippy/tests/ui/collapsible_match2.stderr
new file mode 100644
index 000000000..fe64e4693
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collapsible_match2.stderr
@@ -0,0 +1,97 @@
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match2.rs:13:34
+ |
+LL | Ok(val) if make() => match val {
+ | __________________________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_____________^
+ |
+ = note: `-D clippy::collapsible-match` implied by `-D warnings`
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match2.rs:13:16
+ |
+LL | Ok(val) if make() => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match2.rs:20:24
+ |
+LL | Ok(val) => match val {
+ | ________________________^
+LL | | Some(n) => foo(n),
+LL | | _ => return,
+LL | | },
+ | |_____________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match2.rs:20:16
+ |
+LL | Ok(val) => match val {
+ | ^^^ replace this binding
+LL | Some(n) => foo(n),
+ | ^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match2.rs:34:29
+ |
+LL | $pat => match $e {
+ | _____________________________^
+LL | | $inner_pat => $then,
+LL | | _ => return,
+LL | | },
+ | |_____________________^
+...
+LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
+ | ------------------------------------------------ in this macro invocation
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match2.rs:46:28
+ |
+LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
+ | ^^^ ^^^^^^^ with this pattern
+ | |
+ | replace this binding
+ = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match2.rs:51:20
+ |
+LL | Some(s) => match *s {
+ | ____________________^
+LL | | [n] => foo(n),
+LL | | _ => (),
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match2.rs:51:14
+ |
+LL | Some(s) => match *s {
+ | ^ replace this binding
+LL | [n] => foo(n),
+ | ^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `match`
+ --> $DIR/collapsible_match2.rs:60:24
+ |
+LL | Some(ref s) => match s {
+ | ________________________^
+LL | | [n] => foo(n),
+LL | | _ => (),
+LL | | },
+ | |_________^
+ |
+help: the outer pattern can be modified to include the inner pattern
+ --> $DIR/collapsible_match2.rs:60:14
+ |
+LL | Some(ref s) => match s {
+ | ^^^^^ replace this binding
+LL | [n] => foo(n),
+ | ^^^ with this pattern
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs
new file mode 100644
index 000000000..c12c6a310
--- /dev/null
+++ b/src/tools/clippy/tests/ui/comparison_chain.rs
@@ -0,0 +1,234 @@
+#![allow(dead_code)]
+#![warn(clippy::comparison_chain)]
+
+fn a() {}
+fn b() {}
+fn c() {}
+
+fn f(x: u8, y: u8, z: u8) {
+ // Ignored: Only one branch
+ if x > y {
+ a()
+ }
+
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ }
+
+ // Ignored: Only one explicit conditional
+ if x > y {
+ a()
+ } else {
+ b()
+ }
+
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ } else {
+ c()
+ }
+
+ if x > y {
+ a()
+ } else if y > x {
+ b()
+ } else {
+ c()
+ }
+
+ if x > 1 {
+ a()
+ } else if x < 1 {
+ b()
+ } else if x == 1 {
+ c()
+ }
+
+ // Ignored: Binop args are not equivalent
+ if x > 1 {
+ a()
+ } else if y > 1 {
+ b()
+ } else {
+ c()
+ }
+
+ // Ignored: Binop args are not equivalent
+ if x > y {
+ a()
+ } else if x > z {
+ b()
+ } else if y > z {
+ c()
+ }
+
+ // Ignored: Not binary comparisons
+ if true {
+ a()
+ } else if false {
+ b()
+ } else {
+ c()
+ }
+}
+
+#[allow(clippy::float_cmp)]
+fn g(x: f64, y: f64, z: f64) {
+ // Ignored: f64 doesn't implement Ord
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ }
+
+ // Ignored: f64 doesn't implement Ord
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ } else {
+ c()
+ }
+
+ // Ignored: f64 doesn't implement Ord
+ if x > y {
+ a()
+ } else if y > x {
+ b()
+ } else {
+ c()
+ }
+
+ // Ignored: f64 doesn't implement Ord
+ if x > 1.0 {
+ a()
+ } else if x < 1.0 {
+ b()
+ } else if x == 1.0 {
+ c()
+ }
+}
+
+fn h<T: Ord>(x: T, y: T, z: T) {
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ }
+
+ if x > y {
+ a()
+ } else if x < y {
+ b()
+ } else {
+ c()
+ }
+
+ if x > y {
+ a()
+ } else if y > x {
+ b()
+ } else {
+ c()
+ }
+}
+
+// The following uses should be ignored
+mod issue_5212 {
+ use super::{a, b, c};
+ fn foo() -> u8 {
+ 21
+ }
+
+ fn same_operation_equals() {
+ // operands are fixed
+
+ if foo() == 42 {
+ a()
+ } else if foo() == 42 {
+ b()
+ }
+
+ if foo() == 42 {
+ a()
+ } else if foo() == 42 {
+ b()
+ } else {
+ c()
+ }
+
+ // operands are transposed
+
+ if foo() == 42 {
+ a()
+ } else if 42 == foo() {
+ b()
+ }
+ }
+
+ fn same_operation_not_equals() {
+ // operands are fixed
+
+ if foo() > 42 {
+ a()
+ } else if foo() > 42 {
+ b()
+ }
+
+ if foo() > 42 {
+ a()
+ } else if foo() > 42 {
+ b()
+ } else {
+ c()
+ }
+
+ if foo() < 42 {
+ a()
+ } else if foo() < 42 {
+ b()
+ }
+
+ if foo() < 42 {
+ a()
+ } else if foo() < 42 {
+ b()
+ } else {
+ c()
+ }
+ }
+}
+
+enum Sign {
+ Negative,
+ Positive,
+ Zero,
+}
+
+impl Sign {
+ const fn sign_i8(n: i8) -> Self {
+ if n == 0 {
+ Sign::Zero
+ } else if n > 0 {
+ Sign::Positive
+ } else {
+ Sign::Negative
+ }
+ }
+}
+
+const fn sign_i8(n: i8) -> Sign {
+ if n == 0 {
+ Sign::Zero
+ } else if n > 0 {
+ Sign::Positive
+ } else {
+ Sign::Negative
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr
new file mode 100644
index 000000000..be25a80dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/comparison_chain.stderr
@@ -0,0 +1,97 @@
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:14:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if x < y {
+LL | | b()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::comparison-chain` implied by `-D warnings`
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:27:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if x < y {
+LL | | b()
+LL | | } else {
+LL | | c()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:35:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if y > x {
+LL | | b()
+LL | | } else {
+LL | | c()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:43:5
+ |
+LL | / if x > 1 {
+LL | | a()
+LL | | } else if x < 1 {
+LL | | b()
+LL | | } else if x == 1 {
+LL | | c()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:117:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if x < y {
+LL | | b()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:123:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if x < y {
+LL | | b()
+LL | | } else {
+LL | | c()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: `if` chain can be rewritten with `match`
+ --> $DIR/comparison_chain.rs:131:5
+ |
+LL | / if x > y {
+LL | | a()
+LL | | } else if y > x {
+LL | | b()
+LL | | } else {
+LL | | c()
+LL | | }
+ | |_____^
+ |
+ = help: consider rewriting the `if` chain to use `cmp` and `match`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
new file mode 100644
index 000000000..261024cac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![warn(clippy::comparison_to_empty)]
+
+fn main() {
+ // Disallow comparisons to empty
+ let s = String::new();
+ let _ = s.is_empty();
+ let _ = !s.is_empty();
+
+ let v = vec![0];
+ let _ = v.is_empty();
+ let _ = !v.is_empty();
+
+ // Allow comparisons to non-empty
+ let s = String::new();
+ let _ = s == " ";
+ let _ = s != " ";
+
+ let v = vec![0];
+ let _ = v == [0];
+ let _ = v != [0];
+}
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs
new file mode 100644
index 000000000..98ddd9749
--- /dev/null
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![warn(clippy::comparison_to_empty)]
+
+fn main() {
+ // Disallow comparisons to empty
+ let s = String::new();
+ let _ = s == "";
+ let _ = s != "";
+
+ let v = vec![0];
+ let _ = v == [];
+ let _ = v != [];
+
+ // Allow comparisons to non-empty
+ let s = String::new();
+ let _ = s == " ";
+ let _ = s != " ";
+
+ let v = vec![0];
+ let _ = v == [0];
+ let _ = v != [0];
+}
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
new file mode 100644
index 000000000..f69d6bd52
--- /dev/null
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
@@ -0,0 +1,28 @@
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:8:13
+ |
+LL | let _ = s == "";
+ | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
+ |
+ = note: `-D clippy::comparison-to-empty` implied by `-D warnings`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:9:13
+ |
+LL | let _ = s != "";
+ | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:12:13
+ |
+LL | let _ = v == [];
+ | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:13:13
+ |
+LL | let _ = v != [];
+ | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/copy_iterator.rs b/src/tools/clippy/tests/ui/copy_iterator.rs
new file mode 100644
index 000000000..ae67ebded
--- /dev/null
+++ b/src/tools/clippy/tests/ui/copy_iterator.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::copy_iterator)]
+
+#[derive(Copy, Clone)]
+struct Countdown(u8);
+
+impl Iterator for Countdown {
+ type Item = u8;
+
+ fn next(&mut self) -> Option<u8> {
+ self.0.checked_sub(1).map(|c| {
+ self.0 = c;
+ c
+ })
+ }
+}
+
+fn main() {
+ let my_iterator = Countdown(5);
+ assert_eq!(my_iterator.take(1).count(), 1);
+ assert_eq!(my_iterator.count(), 5);
+}
diff --git a/src/tools/clippy/tests/ui/copy_iterator.stderr b/src/tools/clippy/tests/ui/copy_iterator.stderr
new file mode 100644
index 000000000..f8ce6af79
--- /dev/null
+++ b/src/tools/clippy/tests/ui/copy_iterator.stderr
@@ -0,0 +1,17 @@
+error: you are implementing `Iterator` on a `Copy` type
+ --> $DIR/copy_iterator.rs:6:1
+ |
+LL | / impl Iterator for Countdown {
+LL | | type Item = u8;
+LL | |
+LL | | fn next(&mut self) -> Option<u8> {
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::copy-iterator` implied by `-D warnings`
+ = note: consider implementing `IntoIterator` instead
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
new file mode 100644
index 000000000..948deba3e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
@@ -0,0 +1,13 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
+
+pub trait Trait {
+ const CONSTANT: u8;
+}
+
+impl Trait for u8 {
+ const CONSTANT: u8 = 2;
+}
+
+fn main() {
+ println!("{}", u8::CONSTANT * 10);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs
new file mode 100644
index 000000000..58a20caf6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs
@@ -0,0 +1,9 @@
+pub trait Trait {
+ fn fun(par: &str) -> &str;
+}
+
+impl Trait for str {
+ fn fun(par: &str) -> &str {
+ &par[0..1]
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7272-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7272-aux.rs
new file mode 100644
index 000000000..780797e3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7272-aux.rs
@@ -0,0 +1,14 @@
+pub fn warn<T>(_: T) {}
+
+macro_rules! define_macro {
+ ($d:tt $lower:ident $upper:ident) => {
+ #[macro_export]
+ macro_rules! $upper {
+ ($arg:tt) => {
+ $crate::$lower($arg)
+ };
+ }
+ };
+}
+
+define_macro! {$ warn WARNING}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7868-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7868-aux.rs
new file mode 100644
index 000000000..bee29894b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7868-aux.rs
@@ -0,0 +1,3 @@
+fn zero() {
+ unsafe { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs
new file mode 100644
index 000000000..4afbf027b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs
@@ -0,0 +1,4 @@
+fn zero() {
+ // SAFETY:
+ unsafe { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs
new file mode 100644
index 000000000..95b631513
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs
@@ -0,0 +1,6 @@
+pub fn foo(x: &u32) -> u32 {
+ /* Safety:
+ * This is totally ok.
+ */
+ unsafe { *(x as *const u32) }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
new file mode 100644
index 000000000..5ff2af7cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
@@ -0,0 +1,38 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
+// crates. If we don't set this, compiletest will override the `crate_type` attribute below and
+// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't
+// contain a proc-macro.
+
+#![feature(repr128)]
+#![allow(incomplete_features)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn macro_test(input_stream: TokenStream) -> TokenStream {
+ let first_token = input_stream.into_iter().next().unwrap();
+ let span = first_token.span();
+
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("fn", Span::call_site())),
+ TokenTree::Ident(Ident::new("code", Span::call_site())),
+ TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
+ TokenTree::Group(Group::new(Delimiter::Brace, {
+ let mut clause = Group::new(Delimiter::Brace, TokenStream::new());
+ clause.set_span(span);
+
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("if", Span::call_site())),
+ TokenTree::Ident(Ident::new("true", Span::call_site())),
+ TokenTree::Group(clause.clone()),
+ TokenTree::Ident(Ident::new("else", Span::call_site())),
+ TokenTree::Group(clause),
+ ])
+ })),
+ ])
+}
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs
new file mode 100644
index 000000000..a8a85b4ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs
@@ -0,0 +1,15 @@
+macro_rules! use_self {
+ (
+ impl $ty:ident {
+ fn func(&$this:ident) {
+ [fields($($field:ident)*)]
+ }
+ }
+ ) => (
+ impl $ty {
+ fn func(&$this) {
+ let $ty { $($field),* } = $this;
+ }
+ }
+ )
+}
diff --git a/src/tools/clippy/tests/ui/crashes/cc_seme.rs b/src/tools/clippy/tests/ui/crashes/cc_seme.rs
new file mode 100644
index 000000000..98588be9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/cc_seme.rs
@@ -0,0 +1,27 @@
+#[allow(dead_code)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/478
+
+enum Baz {
+ One,
+ Two,
+}
+
+struct Test {
+ t: Option<usize>,
+ b: Baz,
+}
+
+fn main() {}
+
+pub fn foo() {
+ use Baz::*;
+ let x = Test { t: Some(0), b: One };
+
+ match x {
+ Test { t: Some(_), b: One } => unreachable!(),
+ Test { t: Some(42), b: Two } => unreachable!(),
+ Test { t: None, .. } => unreachable!(),
+ Test { .. } => unreachable!(),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs
new file mode 100644
index 000000000..dca32aa3b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs
@@ -0,0 +1,6 @@
+#![deny(clippy::all)]
+#![allow(unused_imports)]
+
+use std::*;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs
new file mode 100644
index 000000000..b0a3d11bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs
@@ -0,0 +1,13 @@
+#![allow(clippy::all)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
+
+fn main() {
+ match 1 {
+ 1 => {},
+ 2 => {
+ [0; 1];
+ },
+ _ => {},
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs
new file mode 100644
index 000000000..81af88962
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs
@@ -0,0 +1,26 @@
+#![allow(dead_code, unused_variables)]
+
+/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`
+///
+/// Issue: https://github.com/rust-lang/rust-clippy/issues/1782
+use std::{mem, ptr};
+
+fn spanless_eq_ice() {
+ let txt = "something";
+ match txt {
+ "something" => unsafe {
+ ptr::write(
+ ptr::null_mut() as *mut u32,
+ mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
+ )
+ },
+ _ => unsafe {
+ ptr::write(
+ ptr::null_mut() as *mut u32,
+ mem::transmute::<[u8; 4], _>([13, 246, 24, 255]),
+ )
+ },
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs
new file mode 100644
index 000000000..96a8fe6c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs
@@ -0,0 +1,13 @@
+#![allow(clippy::all)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
+
+fn main() {}
+
+pub trait Convert {
+ type Action: From<*const f64>;
+
+ fn convert(val: *const f64) -> Self::Action {
+ val.into()
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2499.rs b/src/tools/clippy/tests/ui/crashes/ice-2499.rs
new file mode 100644
index 000000000..45b3b1869
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2499.rs
@@ -0,0 +1,26 @@
+#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
+
+/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
+///
+/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
+
+fn f(s: &[u8]) -> bool {
+ let t = s[0] as char;
+
+ match t {
+ 'E' | 'W' => {},
+ 'T' => {
+ if s[0..4] != ['0' as u8; 4] {
+ return false;
+ } else {
+ return true;
+ }
+ },
+ _ => {
+ return false;
+ },
+ }
+ true
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2594.rs b/src/tools/clippy/tests/ui/crashes/ice-2594.rs
new file mode 100644
index 000000000..3f3986b6f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2594.rs
@@ -0,0 +1,20 @@
+#![allow(dead_code, unused_variables)]
+
+/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
+///
+/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
+
+fn spanless_hash_ice() {
+ let txt = "something";
+ let empty_header: [u8; 1] = [1; 1];
+
+ match txt {
+ "something" => {
+ let mut headers = [empty_header; 1];
+ },
+ "" => (),
+ _ => (),
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2727.rs b/src/tools/clippy/tests/ui/crashes/ice-2727.rs
new file mode 100644
index 000000000..56024abc8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2727.rs
@@ -0,0 +1,7 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
+
+pub fn f(new: fn()) {
+ new();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2760.rs b/src/tools/clippy/tests/ui/crashes/ice-2760.rs
new file mode 100644
index 000000000..f1a229f3f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2760.rs
@@ -0,0 +1,23 @@
+#![allow(
+ unused_variables,
+ clippy::blacklisted_name,
+ clippy::needless_pass_by_value,
+ dead_code
+)]
+
+/// This should not compile-fail with:
+///
+/// error[E0277]: the trait bound `T: Foo` is not satisfied
+// See rust-lang/rust-clippy#2760.
+
+trait Foo {
+ type Bar;
+}
+
+struct Baz<T: Foo> {
+ bar: T::Bar,
+}
+
+fn take<T: Foo>(baz: Baz<T>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.rs b/src/tools/clippy/tests/ui/crashes/ice-2774.rs
new file mode 100644
index 000000000..88cfa1f92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2774.rs
@@ -0,0 +1,27 @@
+use std::collections::HashSet;
+
+// See rust-lang/rust-clippy#2774.
+
+#[derive(Eq, PartialEq, Debug, Hash)]
+pub struct Bar {
+ foo: Foo,
+}
+
+#[derive(Eq, PartialEq, Debug, Hash)]
+pub struct Foo;
+
+#[allow(clippy::implicit_hasher)]
+// This should not cause a "cannot relate bound region" ICE.
+pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
+ let mut foos = HashSet::new();
+ foos.extend(bars.iter().map(|b| &b.foo));
+}
+
+#[allow(clippy::implicit_hasher)]
+// Also, this should not cause a "cannot relate bound region" ICE.
+pub fn add_barfoos_to_foos2(bars: &HashSet<&Bar>) {
+ let mut foos = HashSet::new();
+ foos.extend(bars.iter().map(|b| &b.foo));
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr
new file mode 100644
index 000000000..0c2d48f93
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr
@@ -0,0 +1,10 @@
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/ice-2774.rs:15:1
+ |
+LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2862.rs b/src/tools/clippy/tests/ui/crashes/ice-2862.rs
new file mode 100644
index 000000000..8326e3663
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2862.rs
@@ -0,0 +1,16 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
+
+pub trait FooMap {
+ fn map<B, F: Fn() -> B>(&self, f: F) -> B;
+}
+
+impl FooMap for bool {
+ fn map<B, F: Fn() -> B>(&self, f: F) -> B {
+ f()
+ }
+}
+
+fn main() {
+ let a = true;
+ a.map(|| false);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs
new file mode 100644
index 000000000..c62981396
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs
@@ -0,0 +1,16 @@
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
+
+struct Ice {
+ size: String,
+}
+
+impl<'a> From<String> for Ice {
+ fn from(_: String) -> Self {
+ let text = || "iceberg".to_string();
+ Self { size: text() }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs
new file mode 100644
index 000000000..268ba86fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs
@@ -0,0 +1,15 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
+
+#[derive(Clone)]
+pub struct HashMap<V, S> {
+ hash_builder: S,
+ table: RawTable<V>,
+}
+
+#[derive(Clone)]
+pub struct RawTable<V> {
+ size: usize,
+ val: V,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
new file mode 100644
index 000000000..02c49aa0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::all)]
+#![allow(clippy::blacklisted_name, clippy::equatable_if_let)]
+#![allow(unused)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
+
+enum Foo {
+ Bar,
+ Baz,
+}
+
+fn bar(foo: Foo) {
+ macro_rules! baz {
+ () => {
+ if let Foo::Bar = foo {}
+ };
+ }
+
+ baz!();
+ baz!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.rs b/src/tools/clippy/tests/ui/crashes/ice-360.rs
new file mode 100644
index 000000000..6555c19ca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-360.rs
@@ -0,0 +1,12 @@
+fn main() {}
+
+fn no_panic<T>(slice: &[T]) {
+ let mut iter = slice.iter();
+ loop {
+ let _ = match iter.next() {
+ Some(ele) => ele,
+ None => break,
+ };
+ loop {}
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
new file mode 100644
index 000000000..0eb7bb12b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
@@ -0,0 +1,25 @@
+error: this loop could be written as a `while let` loop
+ --> $DIR/ice-360.rs:5:5
+ |
+LL | / loop {
+LL | | let _ = match iter.next() {
+LL | | Some(ele) => ele,
+LL | | None => break,
+LL | | };
+LL | | loop {}
+LL | | }
+ | |_____^ help: try: `while let Some(ele) = iter.next() { .. }`
+ |
+ = note: `-D clippy::while-let-loop` implied by `-D warnings`
+
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/ice-360.rs:10:9
+ |
+LL | loop {}
+ | ^^^^^^^
+ |
+ = note: `-D clippy::empty-loop` implied by `-D warnings`
+ = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.rs b/src/tools/clippy/tests/ui/crashes/ice-3717.rs
new file mode 100644
index 000000000..f50714643
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3717.rs
@@ -0,0 +1,10 @@
+#![deny(clippy::implicit_hasher)]
+
+use std::collections::HashSet;
+
+fn main() {}
+
+pub fn ice_3717(_: &HashSet<usize>) {
+ let _ = [0u8; 0];
+ let _: HashSet<usize> = HashSet::new();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr
new file mode 100644
index 000000000..4d3d617b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr
@@ -0,0 +1,22 @@
+error: parameter of type `HashSet` should be generalized over different hashers
+ --> $DIR/ice-3717.rs:7:21
+ |
+LL | pub fn ice_3717(_: &HashSet<usize>) {
+ | ^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/ice-3717.rs:1:9
+ |
+LL | #![deny(clippy::implicit_hasher)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+help: consider adding a type parameter
+ |
+LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | let _: HashSet<usize> = HashSet::default();
+ | ~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3741.rs b/src/tools/clippy/tests/ui/crashes/ice-3741.rs
new file mode 100644
index 000000000..1253ddcfa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3741.rs
@@ -0,0 +1,10 @@
+// aux-build:proc_macro_crash.rs
+
+#![warn(clippy::suspicious_else_formatting)]
+
+extern crate proc_macro_crash;
+use proc_macro_crash::macro_test;
+
+fn main() {
+ macro_test!(2);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3747.rs b/src/tools/clippy/tests/ui/crashes/ice-3747.rs
new file mode 100644
index 000000000..cdf018cbc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3747.rs
@@ -0,0 +1,17 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
+
+macro_rules! a {
+ ( $pub:tt $($attr:tt)* ) => {
+ $($attr)* $pub fn say_hello() {}
+ };
+}
+
+macro_rules! b {
+ () => {
+ a! { pub }
+ };
+}
+
+b! {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.rs b/src/tools/clippy/tests/ui/crashes/ice-3891.rs
new file mode 100644
index 000000000..05c5134c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3891.rs
@@ -0,0 +1,3 @@
+fn main() {
+ 1x;
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.stderr b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr
new file mode 100644
index 000000000..59469ec58
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr
@@ -0,0 +1,10 @@
+error: invalid suffix `x` for number literal
+ --> $DIR/ice-3891.rs:2:5
+ |
+LL | 1x;
+ | ^^ invalid suffix `x`
+ |
+ = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3969.rs b/src/tools/clippy/tests/ui/crashes/ice-3969.rs
new file mode 100644
index 000000000..9b68cac7f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3969.rs
@@ -0,0 +1,50 @@
+// https://github.com/rust-lang/rust-clippy/issues/3969
+// used to crash: error: internal compiler error:
+// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `<i32 as
+// std::iter::Iterator>::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
+
+// Check that tautalogically false bounds are accepted, and are used
+// in type inference.
+#![feature(trivial_bounds)]
+#![allow(unused)]
+trait A {}
+
+impl A for i32 {}
+
+struct Dst<X: ?Sized> {
+ x: X,
+}
+
+struct TwoStrs(str, str)
+where
+ str: Sized;
+
+fn unsized_local()
+where
+ for<'a> Dst<dyn A + 'a>: Sized,
+{
+ let x: Dst<dyn A> = *(Box::new(Dst { x: 1 }) as Box<Dst<dyn A>>);
+}
+
+fn return_str() -> str
+where
+ str: Sized,
+{
+ *"Sized".to_string().into_boxed_str()
+}
+
+fn use_op(s: String) -> String
+where
+ String: ::std::ops::Neg<Output = String>,
+{
+ -s
+}
+
+fn use_for()
+where
+ i32: Iterator,
+{
+ for _ in 2i32 {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3969.stderr b/src/tools/clippy/tests/ui/crashes/ice-3969.stderr
new file mode 100644
index 000000000..790180808
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3969.stderr
@@ -0,0 +1,34 @@
+error: trait bound str: std::marker::Sized does not depend on any type or lifetime parameters
+ --> $DIR/ice-3969.rs:20:10
+ |
+LL | str: Sized;
+ | ^^^^^
+ |
+ = note: `-D trivial-bounds` implied by `-D warnings`
+
+error: trait bound for<'a> Dst<(dyn A + 'a)>: std::marker::Sized does not depend on any type or lifetime parameters
+ --> $DIR/ice-3969.rs:24:30
+ |
+LL | for<'a> Dst<dyn A + 'a>: Sized,
+ | ^^^^^
+
+error: trait bound str: std::marker::Sized does not depend on any type or lifetime parameters
+ --> $DIR/ice-3969.rs:31:10
+ |
+LL | str: Sized,
+ | ^^^^^
+
+error: trait bound std::string::String: std::ops::Neg does not depend on any type or lifetime parameters
+ --> $DIR/ice-3969.rs:38:13
+ |
+LL | String: ::std::ops::Neg<Output = String>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: trait bound i32: std::iter::Iterator does not depend on any type or lifetime parameters
+ --> $DIR/ice-3969.rs:45:10
+ |
+LL | i32: Iterator,
+ | ^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4121.rs b/src/tools/clippy/tests/ui/crashes/ice-4121.rs
new file mode 100644
index 000000000..e1a142fdc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4121.rs
@@ -0,0 +1,13 @@
+use std::mem;
+
+pub struct Foo<A, B>(A, B);
+
+impl<A, B> Foo<A, B> {
+ const HOST_SIZE: usize = mem::size_of::<B>();
+
+ pub fn crash() -> bool {
+ Self::HOST_SIZE == 0
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4545.rs b/src/tools/clippy/tests/ui/crashes/ice-4545.rs
new file mode 100644
index 000000000..d9c9c2096
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4545.rs
@@ -0,0 +1,14 @@
+fn repro() {
+ trait Foo {
+ type Bar;
+ }
+
+ #[allow(dead_code)]
+ struct Baz<T: Foo> {
+ field: T::Bar,
+ }
+}
+
+fn main() {
+ repro();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4579.rs b/src/tools/clippy/tests/ui/crashes/ice-4579.rs
new file mode 100644
index 000000000..2e7e279f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4579.rs
@@ -0,0 +1,13 @@
+#![allow(clippy::single_match)]
+
+use std::ptr;
+
+fn main() {
+ match Some(0_usize) {
+ Some(_) => {
+ let s = "012345";
+ unsafe { ptr::read(s.as_ptr().offset(1) as *const [u8; 5]) };
+ },
+ _ => (),
+ };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4671.rs b/src/tools/clippy/tests/ui/crashes/ice-4671.rs
new file mode 100644
index 000000000..64e8e7769
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4671.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::use_self)]
+
+#[macro_use]
+#[path = "auxiliary/use_self_macro.rs"]
+mod use_self_macro;
+
+struct Foo {
+ a: u32,
+}
+
+use_self! {
+ impl Foo {
+ fn func(&self) {
+ [fields(
+ a
+ )]
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4727.rs b/src/tools/clippy/tests/ui/crashes/ice-4727.rs
new file mode 100644
index 000000000..2a4bc83f5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4727.rs
@@ -0,0 +1,6 @@
+#![warn(clippy::use_self)]
+
+#[path = "auxiliary/ice-4727-aux.rs"]
+mod aux;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4760.rs b/src/tools/clippy/tests/ui/crashes/ice-4760.rs
new file mode 100644
index 000000000..08b069617
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4760.rs
@@ -0,0 +1,9 @@
+const COUNT: usize = 2;
+struct Thing;
+trait Dummy {}
+
+const _: () = {
+ impl Dummy for Thing where [i32; COUNT]: Sized {}
+};
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4775.rs b/src/tools/clippy/tests/ui/crashes/ice-4775.rs
new file mode 100644
index 000000000..405e3039e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4775.rs
@@ -0,0 +1,11 @@
+pub struct ArrayWrapper<const N: usize>([usize; N]);
+
+impl<const N: usize> ArrayWrapper<{ N }> {
+ pub fn ice(&self) {
+ for i in self.0.iter() {
+ println!("{}", i);
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-4968.rs b/src/tools/clippy/tests/ui/crashes/ice-4968.rs
new file mode 100644
index 000000000..e0510d942
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-4968.rs
@@ -0,0 +1,21 @@
+// check-pass
+
+// Test for https://github.com/rust-lang/rust-clippy/issues/4968
+
+#![warn(clippy::unsound_collection_transmute)]
+#![allow(clippy::transmute_undefined_repr)]
+
+trait Trait {
+ type Assoc;
+}
+
+use std::mem::{self, ManuallyDrop};
+
+#[allow(unused)]
+fn func<T: Trait>(slice: Vec<T::Assoc>) {
+ unsafe {
+ let _: Vec<ManuallyDrop<T::Assoc>> = mem::transmute(slice);
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5207.rs b/src/tools/clippy/tests/ui/crashes/ice-5207.rs
new file mode 100644
index 000000000..f463f78a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5207.rs
@@ -0,0 +1,5 @@
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207
+
+pub async fn bar<'a, T: 'a>(_: T) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5223.rs b/src/tools/clippy/tests/ui/crashes/ice-5223.rs
new file mode 100644
index 000000000..e3b3b27a6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5223.rs
@@ -0,0 +1,15 @@
+// Regression test for #5233
+#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)]
+
+pub struct KotomineArray<T, const N: usize> {
+ arr: [T; N],
+}
+
+impl<T: std::clone::Clone, const N: usize> KotomineArray<T, N> {
+ pub fn ice(self) {
+ let _ = self.arr[..];
+ let _ = self.arr.iter().cloned().collect::<Vec<_>>();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5238.rs b/src/tools/clippy/tests/ui/crashes/ice-5238.rs
new file mode 100644
index 000000000..989eb6d44
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5238.rs
@@ -0,0 +1,9 @@
+// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562
+
+#![feature(generators, generator_trait)]
+
+fn main() {
+ let _ = || {
+ yield;
+ };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5389.rs b/src/tools/clippy/tests/ui/crashes/ice-5389.rs
new file mode 100644
index 000000000..de2621990
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5389.rs
@@ -0,0 +1,13 @@
+#![allow(clippy::explicit_counter_loop)]
+
+fn main() {
+ let v = vec![1, 2, 3];
+ let mut i = 0;
+ let max_storage_size = [0; 128 * 1024];
+ for item in &v {
+ bar(i, *item);
+ i += 1;
+ }
+}
+
+fn bar(_: usize, _: u32) {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.rs b/src/tools/clippy/tests/ui/crashes/ice-5497.rs
new file mode 100644
index 000000000..0769bce5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5497.rs
@@ -0,0 +1,11 @@
+// reduced from rustc issue-69020-assoc-const-arith-overflow.rs
+pub fn main() {}
+
+pub trait Foo {
+ const OOB: i32;
+}
+
+impl<T: Foo> Foo for Vec<T> {
+ const OOB: i32 = [1][1] + T::OOB;
+ //~^ ERROR operation will panic
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.stderr b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr
new file mode 100644
index 000000000..e75e7dc91
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5497.stderr
@@ -0,0 +1,10 @@
+error: this operation will panic at runtime
+ --> $DIR/ice-5497.rs:9:22
+ |
+LL | const OOB: i32 = [1][1] + T::OOB;
+ | ^^^^^^ index out of bounds: the length is 1 but the index is 1
+ |
+ = note: `#[deny(unconditional_panic)]` on by default
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5579.rs b/src/tools/clippy/tests/ui/crashes/ice-5579.rs
new file mode 100644
index 000000000..e1842c73f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5579.rs
@@ -0,0 +1,17 @@
+trait IsErr {
+ fn is_err(&self, err: &str) -> bool;
+}
+
+impl<T> IsErr for Option<T> {
+ fn is_err(&self, _err: &str) -> bool {
+ true
+ }
+}
+
+fn main() {
+ let t = Some(1);
+
+ if t.is_err("") {
+ t.unwrap();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.rs b/src/tools/clippy/tests/ui/crashes/ice-5835.rs
new file mode 100644
index 000000000..5e99cb432
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5835.rs
@@ -0,0 +1,9 @@
+#[rustfmt::skip]
+pub struct Foo {
+ /// 位
+ /// ^ Do not remove this tab character.
+ /// It was required to trigger the ICE.
+ pub bar: u8,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.stderr b/src/tools/clippy/tests/ui/crashes/ice-5835.stderr
new file mode 100644
index 000000000..c972bcb60
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5835.stderr
@@ -0,0 +1,10 @@
+error: using tabs in doc comments is not recommended
+ --> $DIR/ice-5835.rs:3:10
+ |
+LL | /// 位
+ | ^^^^ help: consider using four spaces per tab
+ |
+ = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.rs b/src/tools/clippy/tests/ui/crashes/ice-5872.rs
new file mode 100644
index 000000000..68afa8f8c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5872.rs
@@ -0,0 +1,5 @@
+#![warn(clippy::needless_collect)]
+
+fn main() {
+ let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.stderr b/src/tools/clippy/tests/ui/crashes/ice-5872.stderr
new file mode 100644
index 000000000..a60ca345c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5872.stderr
@@ -0,0 +1,10 @@
+error: avoid using `collect()` when not needed
+ --> $DIR/ice-5872.rs:4:39
+ |
+LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+ |
+ = note: `-D clippy::needless-collect` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5944.rs b/src/tools/clippy/tests/ui/crashes/ice-5944.rs
new file mode 100644
index 000000000..ce46bc1ac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5944.rs
@@ -0,0 +1,14 @@
+#![warn(clippy::repeat_once)]
+#![allow(clippy::let_unit_value)]
+
+trait Repeat {
+ fn repeat(&self) {}
+}
+
+impl Repeat for usize {
+ fn repeat(&self) {}
+}
+
+fn main() {
+ let _ = 42.repeat();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6139.rs b/src/tools/clippy/tests/ui/crashes/ice-6139.rs
new file mode 100644
index 000000000..f3966e47f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6139.rs
@@ -0,0 +1,7 @@
+trait T<'a> {}
+
+fn foo(_: Vec<Box<dyn T<'_>>>) {}
+
+fn main() {
+ foo(vec![]);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6153.rs b/src/tools/clippy/tests/ui/crashes/ice-6153.rs
new file mode 100644
index 000000000..9f73f39f1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6153.rs
@@ -0,0 +1,9 @@
+pub struct S<'a, 'e>(&'a str, &'e str);
+
+pub type T<'a, 'e> = std::collections::HashMap<S<'a, 'e>, ()>;
+
+impl<'e, 'a: 'e> S<'a, 'e> {
+ pub fn foo(_a: &str, _b: &str, _map: &T) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
new file mode 100644
index 000000000..4fe92d356
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
@@ -0,0 +1,21 @@
+//! This is a minimal reproducer for the ICE in https://github.com/rust-lang/rust-clippy/pull/6179.
+//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+
+struct Foo;
+
+impl Foo {
+ fn new() -> Self {
+ impl Foo {
+ fn bar() {}
+ }
+
+ let _: _ = 1;
+
+ Self {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6250.rs b/src/tools/clippy/tests/ui/crashes/ice-6250.rs
new file mode 100644
index 000000000..c33580ff6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6250.rs
@@ -0,0 +1,16 @@
+// originally from glacier/fixed/77218.rs
+// ice while adjusting...
+
+pub struct Cache {
+ data: Vec<i32>,
+}
+
+pub fn list_data(cache: &Cache, key: usize) {
+ for reference in vec![1, 2, 3] {
+ if
+ /* let */
+ Some(reference) = cache.data.get(key) {
+ unimplemented!()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
new file mode 100644
index 000000000..878897c41
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
@@ -0,0 +1,30 @@
+error[E0601]: `main` function not found in crate `ice_6250`
+ --> $DIR/ice-6250.rs:16:2
+ |
+LL | }
+ | ^ consider adding a `main` function to `$DIR/ice-6250.rs`
+
+error[E0308]: mismatched types
+ --> $DIR/ice-6250.rs:12:14
+ |
+LL | for reference in vec![1, 2, 3] {
+ | --------- expected due to the type of this binding
+...
+LL | Some(reference) = cache.data.get(key) {
+ | ^^^^^^^^^ expected integer, found `&i32`
+ |
+help: consider dereferencing the borrow
+ |
+LL | Some(*reference) = cache.data.get(key) {
+ | +
+
+error[E0308]: mismatched types
+ --> $DIR/ice-6250.rs:12:9
+ |
+LL | Some(reference) = cache.data.get(key) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0601.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.rs b/src/tools/clippy/tests/ui/crashes/ice-6251.rs
new file mode 100644
index 000000000..6aa779aae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6251.rs
@@ -0,0 +1,6 @@
+// originally from glacier/fixed/77329.rs
+// assertion failed: `(left == right) ; different DefIds
+
+fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ std::iter::empty()
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr
new file mode 100644
index 000000000..8da2965c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr
@@ -0,0 +1,41 @@
+error[E0601]: `main` function not found in crate `ice_6251`
+ --> $DIR/ice-6251.rs:6:2
+ |
+LL | }
+ | ^ consider adding a `main` function to `$DIR/ice-6251.rs`
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+ --> $DIR/ice-6251.rs:4:45
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^ doesn't have a size known at compile-time
+ |
+ = help: the trait `std::marker::Sized` is not implemented for `[u8]`
+ = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> {
+ | +
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+ --> $DIR/ice-6251.rs:4:54
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^ doesn't have a size known at compile-time
+ |
+ = help: the trait `std::marker::Sized` is not implemented for `[u8]`
+ = note: the return type of a function must have a statically known size
+
+error[E0308]: mismatched types
+ --> $DIR/ice-6251.rs:4:44
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^^^^^^^^^^^ expected `usize`, found closure
+ |
+ = note: expected type `usize`
+ found closure `[closure@$DIR/ice-6251.rs:4:44: 4:53]`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0277, E0308, E0601.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.rs b/src/tools/clippy/tests/ui/crashes/ice-6252.rs
new file mode 100644
index 000000000..0ccf0aae9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.rs
@@ -0,0 +1,14 @@
+// originally from glacier fixed/77919.rs
+// encountered errors resolving bounds after type-checking
+trait TypeVal<T> {
+ const VAL: T;
+}
+struct Five;
+struct Multiply<N, M> {
+ _n: PhantomData,
+}
+impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+
+fn main() {
+ [1; <Multiply<Five, Five>>::VAL];
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
new file mode 100644
index 000000000..638e4a548
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
@@ -0,0 +1,36 @@
+error[E0412]: cannot find type `PhantomData` in this scope
+ --> $DIR/ice-6252.rs:8:9
+ |
+LL | _n: PhantomData,
+ | ^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing one of these items
+ |
+LL | use core::marker::PhantomData;
+ |
+LL | use serde::__private::PhantomData;
+ |
+LL | use std::marker::PhantomData;
+ |
+
+error[E0412]: cannot find type `VAL` in this scope
+ --> $DIR/ice-6252.rs:10:63
+ |
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | - ^^^ not found in this scope
+ | |
+ | help: you might be missing a type parameter: `, VAL`
+
+error[E0046]: not all trait items implemented, missing: `VAL`
+ --> $DIR/ice-6252.rs:10:1
+ |
+LL | const VAL: T;
+ | ------------ `VAL` from trait
+...
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0046, E0412.
+For more information about an error, try `rustc --explain E0046`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6254.rs b/src/tools/clippy/tests/ui/crashes/ice-6254.rs
new file mode 100644
index 000000000..a2a60a169
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6254.rs
@@ -0,0 +1,16 @@
+// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs
+// panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())',
+// compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5
+
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(PartialEq)]
+struct Foo(i32);
+const FOO_REF_REF: &&Foo = &&Foo(42);
+
+fn main() {
+ // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071)
+ match FOO_REF_REF {
+ FOO_REF_REF => {},
+ Foo(_) => {},
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6254.stderr b/src/tools/clippy/tests/ui/crashes/ice-6254.stderr
new file mode 100644
index 000000000..f37ab2e9b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6254.stderr
@@ -0,0 +1,12 @@
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/ice-6254.rs:13:9
+ |
+LL | FOO_REF_REF => {},
+ | ^^^^^^^^^^^
+ |
+ = note: `-D indirect-structural-match` implied by `-D warnings`
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.rs b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
new file mode 100644
index 000000000..bd4a81d98
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
@@ -0,0 +1,15 @@
+// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs
+// inconsistent resolution for a macro
+
+macro_rules! define_other_core {
+ ( ) => {
+ extern crate std as core;
+ //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern`
+ };
+}
+
+fn main() {
+ core::panic!();
+}
+
+define_other_core!();
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.stderr b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
new file mode 100644
index 000000000..db0cb25e3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
@@ -0,0 +1,13 @@
+error: macro-expanded `extern crate` items cannot shadow names passed with `--extern`
+ --> $DIR/ice-6255.rs:6:9
+ |
+LL | extern crate std as core;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+ | -------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.rs b/src/tools/clippy/tests/ui/crashes/ice-6256.rs
new file mode 100644
index 000000000..67308263d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6256.rs
@@ -0,0 +1,15 @@
+// originally from rustc ./src/test/ui/regions/issue-78262.rs
+// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig()
+#![allow(clippy::upper_case_acronyms)]
+
+trait TT {}
+
+impl dyn TT {
+ fn func(&self) {}
+}
+
+#[rustfmt::skip]
+fn main() {
+ let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+ //[nll]~^ ERROR: borrowed data escapes outside of closure
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr
new file mode 100644
index 000000000..9cfcccf1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr
@@ -0,0 +1,14 @@
+error[E0521]: borrowed data escapes outside of closure
+ --> $DIR/ice-6256.rs:13:26
+ |
+LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+ | - - ^^^^^^^^
+ | | | |
+ | | | `x` escapes the closure body here
+ | | | argument requires that `'1` must outlive `'static`
+ | | let's call the lifetime of this reference `'1`
+ | `x` is a reference that is only valid in the closure body
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6332.rs b/src/tools/clippy/tests/ui/crashes/ice-6332.rs
new file mode 100644
index 000000000..9dc92aa50
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6332.rs
@@ -0,0 +1,11 @@
+fn cmark_check() {
+ let mut link_err = false;
+ macro_rules! cmark_error {
+ ($bad:expr) => {
+ *$bad = true;
+ };
+ }
+ cmark_error!(&mut link_err);
+}
+
+pub fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6539.rs b/src/tools/clippy/tests/ui/crashes/ice-6539.rs
new file mode 100644
index 000000000..ac6c3e4ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6539.rs
@@ -0,0 +1,16 @@
+// The test for the ICE 6539: https://github.com/rust-lang/rust-clippy/issues/6539.
+// The cause is that `zero_sized_map_values` used `layout_of` with types from type aliases,
+// which is essentially the same as the ICE 4968.
+// Note that only type aliases with associated types caused the crash this time,
+// not others such as trait impls.
+
+use std::collections::{BTreeMap, HashMap};
+
+pub trait Trait {
+ type Assoc;
+}
+
+type TypeAlias<T> = HashMap<(), <T as Trait>::Assoc>;
+type TypeAlias2<T> = BTreeMap<(), <T as Trait>::Assoc>;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6792.rs b/src/tools/clippy/tests/ui/crashes/ice-6792.rs
new file mode 100644
index 000000000..9cbafc716
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6792.rs
@@ -0,0 +1,20 @@
+//! This is a reproducer for the ICE 6792: https://github.com/rust-lang/rust-clippy/issues/6792.
+//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`.
+
+trait Trait {
+ type Ty;
+
+ fn broken() -> Self::Ty;
+}
+
+struct Foo;
+
+impl Trait for Foo {
+ type Ty = Foo;
+
+ fn broken() -> Self::Ty {
+ Self::Ty {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6793.rs b/src/tools/clippy/tests/ui/crashes/ice-6793.rs
new file mode 100644
index 000000000..12a4a0d25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6793.rs
@@ -0,0 +1,23 @@
+//! This is a reproducer for the ICE 6793: https://github.com/rust-lang/rust-clippy/issues/6793.
+//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`, which is the same as the ICE 6792.
+
+trait Trait {
+ type Ty: 'static + Clone;
+
+ fn broken() -> Self::Ty;
+}
+
+#[derive(Clone)]
+struct MyType {
+ x: i32,
+}
+
+impl Trait for MyType {
+ type Ty = MyType;
+
+ fn broken() -> Self::Ty {
+ Self::Ty { x: 1 }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6840.rs b/src/tools/clippy/tests/ui/crashes/ice-6840.rs
new file mode 100644
index 000000000..d789f60c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-6840.rs
@@ -0,0 +1,31 @@
+//! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840.
+//! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+pub trait Rule {
+ type DependencyKey;
+}
+
+pub struct RuleEdges<R: Rule> {
+ dependencies: R::DependencyKey,
+}
+
+type RuleDependencyEdges<R> = HashMap<u32, RuleEdges<R>>;
+
+// reproducer from the GitHub issue ends here
+// but check some additional variants
+type RuleDependencyEdgesArray<R> = HashMap<u32, [RuleEdges<R>; 8]>;
+type RuleDependencyEdgesSlice<R> = HashMap<u32, &'static [RuleEdges<R>]>;
+type RuleDependencyEdgesRef<R> = HashMap<u32, &'static RuleEdges<R>>;
+type RuleDependencyEdgesRaw<R> = HashMap<u32, *const RuleEdges<R>>;
+type RuleDependencyEdgesTuple<R> = HashMap<u32, (RuleEdges<R>, RuleEdges<R>)>;
+
+// and an additional checks to make sure fix doesn't have stack-overflow issue
+// on self-containing types
+pub struct SelfContaining {
+ inner: Box<SelfContaining>,
+}
+type SelfContainingEdges = HashMap<u32, SelfContaining>;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs
new file mode 100644
index 000000000..0cbceedbd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs
@@ -0,0 +1,9 @@
+#![deny(clippy::all)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/700
+
+fn core() {}
+
+fn main() {
+ core();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7012.rs b/src/tools/clippy/tests/ui/crashes/ice-7012.rs
new file mode 100644
index 000000000..60bdbc4f1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7012.rs
@@ -0,0 +1,17 @@
+#![allow(clippy::all)]
+
+enum _MyOption {
+ None,
+ Some(()),
+}
+
+impl _MyOption {
+ fn _foo(&self) {
+ match self {
+ &Self::Some(_) => {},
+ _ => {},
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7126.rs b/src/tools/clippy/tests/ui/crashes/ice-7126.rs
new file mode 100644
index 000000000..ca563ba09
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7126.rs
@@ -0,0 +1,14 @@
+// This test requires a feature gated const fn and will stop working in the future.
+
+#![feature(const_btree_new)]
+
+use std::collections::BTreeMap;
+
+struct Foo(BTreeMap<i32, i32>);
+impl Foo {
+ fn new() -> Self {
+ Self(BTreeMap::new())
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.rs b/src/tools/clippy/tests/ui/crashes/ice-7169.rs
new file mode 100644
index 000000000..82095febc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7169.rs
@@ -0,0 +1,9 @@
+#[derive(Default)]
+struct A<T> {
+ a: Vec<A<T>>,
+ b: T,
+}
+
+fn main() {
+ if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.stderr b/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
new file mode 100644
index 000000000..5a9cd3238
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
@@ -0,0 +1,10 @@
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/ice-7169.rs:8:12
+ |
+LL | if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
+ | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::<String>::default()).is_ok()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7231.rs b/src/tools/clippy/tests/ui/crashes/ice-7231.rs
new file mode 100644
index 000000000..4ad0d3513
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7231.rs
@@ -0,0 +1,9 @@
+#![allow(clippy::never_loop)]
+
+async fn f() {
+ loop {
+ break;
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7272.rs b/src/tools/clippy/tests/ui/crashes/ice-7272.rs
new file mode 100644
index 000000000..57ab6ca14
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7272.rs
@@ -0,0 +1,12 @@
+// aux-build:ice-7272-aux.rs
+
+#![allow(clippy::no_effect)]
+
+extern crate ice_7272_aux;
+
+use ice_7272_aux::*;
+
+pub fn main() {
+ || WARNING!("Style changed!");
+ || "}{";
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7340.rs b/src/tools/clippy/tests/ui/crashes/ice-7340.rs
new file mode 100644
index 000000000..7d2351d60
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7340.rs
@@ -0,0 +1,6 @@
+#![allow(clippy::no_effect)]
+
+fn main() {
+ const CONSTANT: usize = 8;
+ [1; 1 % CONSTANT];
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
new file mode 100644
index 000000000..85fa42103
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
@@ -0,0 +1,32 @@
+// compile-flags: -Clink-arg=-nostartfiles
+// ignore-macos
+// ignore-windows
+
+#![feature(lang_items, start, libc)]
+#![no_std]
+#![allow(clippy::if_same_then_else)]
+#![allow(clippy::redundant_pattern_matching)]
+
+use core::panic::PanicInfo;
+
+struct S;
+
+impl Drop for S {
+ fn drop(&mut self) {}
+}
+
+#[start]
+fn main(argc: isize, argv: *const *const u8) -> isize {
+ if let Some(_) = Some(S) {
+ } else {
+ }
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7423.rs b/src/tools/clippy/tests/ui/crashes/ice-7423.rs
new file mode 100644
index 000000000..31340b012
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7423.rs
@@ -0,0 +1,13 @@
+pub trait Trait {
+ fn f();
+}
+
+impl Trait for usize {
+ fn f() {
+ extern "C" {
+ fn g() -> usize;
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7868.rs b/src/tools/clippy/tests/ui/crashes/ice-7868.rs
new file mode 100644
index 000000000..c6932164e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7868.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::no_effect)]
+
+#[path = "auxiliary/ice-7868-aux.rs"]
+mod zero;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7868.stderr b/src/tools/clippy/tests/ui/crashes/ice-7868.stderr
new file mode 100644
index 000000000..1a33e6475
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7868.stderr
@@ -0,0 +1,11 @@
+error: unsafe block missing a safety comment
+ --> $DIR/auxiliary/ice-7868-aux.rs:2:5
+ |
+LL | unsafe { 0 };
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
+ = help: consider adding a safety comment on the preceding line
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7869.rs b/src/tools/clippy/tests/ui/crashes/ice-7869.rs
new file mode 100644
index 000000000..8f97a063a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7869.rs
@@ -0,0 +1,7 @@
+enum Tila {
+ TyöAlkoi,
+ TyöKeskeytyi,
+ TyöValmis,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7869.stderr b/src/tools/clippy/tests/ui/crashes/ice-7869.stderr
new file mode 100644
index 000000000..4fa9fb27e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7869.stderr
@@ -0,0 +1,15 @@
+error: all variants have the same prefix: `Työ`
+ --> $DIR/ice-7869.rs:1:1
+ |
+LL | / enum Tila {
+LL | | TyöAlkoi,
+LL | | TyöKeskeytyi,
+LL | | TyöValmis,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7934.rs b/src/tools/clippy/tests/ui/crashes/ice-7934.rs
new file mode 100644
index 000000000..a4691c413
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7934.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::no_effect)]
+
+#[path = "auxiliary/ice-7934-aux.rs"]
+mod zero;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8250.rs b/src/tools/clippy/tests/ui/crashes/ice-8250.rs
new file mode 100644
index 000000000..d9a5ee116
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8250.rs
@@ -0,0 +1,6 @@
+fn _f(s: &str) -> Option<()> {
+ let _ = s[1..].splitn(2, '.').next()?;
+ Some(())
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8250.stderr b/src/tools/clippy/tests/ui/crashes/ice-8250.stderr
new file mode 100644
index 000000000..8ed8f3b3a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8250.stderr
@@ -0,0 +1,10 @@
+error: unnecessary use of `splitn`
+ --> $DIR/ice-8250.rs:2:13
+ |
+LL | let _ = s[1..].splitn(2, '.').next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')`
+ |
+ = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8386.rs b/src/tools/clippy/tests/ui/crashes/ice-8386.rs
new file mode 100644
index 000000000..3e38b1408
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8386.rs
@@ -0,0 +1,3 @@
+fn f(x: u32, mut arg: &String) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8681.rs b/src/tools/clippy/tests/ui/crashes/ice-8681.rs
new file mode 100644
index 000000000..ee14f011f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8681.rs
@@ -0,0 +1,10 @@
+// aux-build: ice-8681-aux.rs
+
+#![warn(clippy::undocumented_unsafe_blocks)]
+
+#[path = "auxiliary/ice-8681-aux.rs"]
+mod ice_8681_aux;
+
+fn main() {
+ let _ = ice_8681_aux::foo(&0u32);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8821.rs b/src/tools/clippy/tests/ui/crashes/ice-8821.rs
new file mode 100644
index 000000000..fb87b79ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8821.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::let_unit_value)]
+
+fn f() {}
+static FN: fn() = f;
+
+fn main() {
+ let _: () = FN();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8821.stderr b/src/tools/clippy/tests/ui/crashes/ice-8821.stderr
new file mode 100644
index 000000000..486096e0a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8821.stderr
@@ -0,0 +1,10 @@
+error: this let-binding has unit value
+ --> $DIR/ice-8821.rs:7:5
+ |
+LL | let _: () = FN();
+ | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();`
+ |
+ = note: `-D clippy::let-unit-value` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8850.rs b/src/tools/clippy/tests/ui/crashes/ice-8850.rs
new file mode 100644
index 000000000..f2747ab22
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8850.rs
@@ -0,0 +1,27 @@
+fn fn_pointer_static() -> usize {
+ static FN: fn() -> usize = || 1;
+ let res = FN() + 1;
+ res
+}
+
+fn fn_pointer_const() -> usize {
+ const FN: fn() -> usize = || 1;
+ let res = FN() + 1;
+ res
+}
+
+fn deref_to_dyn_fn() -> usize {
+ struct Derefs;
+ impl std::ops::Deref for Derefs {
+ type Target = dyn Fn() -> usize;
+
+ fn deref(&self) -> &Self::Target {
+ &|| 2
+ }
+ }
+ static FN: Derefs = Derefs;
+ let res = FN() + 1;
+ res
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8850.stderr b/src/tools/clippy/tests/ui/crashes/ice-8850.stderr
new file mode 100644
index 000000000..620fd1eda
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-8850.stderr
@@ -0,0 +1,45 @@
+error: returning the result of a `let` binding from a block
+ --> $DIR/ice-8850.rs:4:5
+ |
+LL | let res = FN() + 1;
+ | ------------------- unnecessary `let` binding
+LL | res
+ | ^^^
+ |
+ = note: `-D clippy::let-and-return` implied by `-D warnings`
+help: return the expression directly
+ |
+LL ~
+LL ~ FN() + 1
+ |
+
+error: returning the result of a `let` binding from a block
+ --> $DIR/ice-8850.rs:10:5
+ |
+LL | let res = FN() + 1;
+ | ------------------- unnecessary `let` binding
+LL | res
+ | ^^^
+ |
+help: return the expression directly
+ |
+LL ~
+LL ~ FN() + 1
+ |
+
+error: returning the result of a `let` binding from a block
+ --> $DIR/ice-8850.rs:24:5
+ |
+LL | let res = FN() + 1;
+ | ------------------- unnecessary `let` binding
+LL | res
+ | ^^^
+ |
+help: return the expression directly
+ |
+LL ~
+LL ~ FN() + 1
+ |
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9041.rs b/src/tools/clippy/tests/ui/crashes/ice-9041.rs
new file mode 100644
index 000000000..55cc9bc99
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-9041.rs
@@ -0,0 +1,8 @@
+pub struct Thing;
+
+pub fn has_thing(things: &[Thing]) -> bool {
+ let is_thing_ready = |_peer: &Thing| -> bool { todo!() };
+ things.iter().find(|p| is_thing_ready(p)).is_some()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9041.stderr b/src/tools/clippy/tests/ui/crashes/ice-9041.stderr
new file mode 100644
index 000000000..f5038f0a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-9041.stderr
@@ -0,0 +1,10 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/ice-9041.rs:5:19
+ |
+LL | things.iter().find(|p| is_thing_ready(p)).is_some()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9238.rs b/src/tools/clippy/tests/ui/crashes/ice-9238.rs
new file mode 100644
index 000000000..ee6abd519
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-9238.rs
@@ -0,0 +1,12 @@
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+#![warn(clippy::branches_sharing_code)]
+
+const fn f() -> usize {
+ 2
+}
+const C: [f64; f()] = [0f64; f()];
+
+fn main() {
+ let _ = if true { C[0] } else { C[1] };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9242.rs b/src/tools/clippy/tests/ui/crashes/ice-9242.rs
new file mode 100644
index 000000000..0099e6e2f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-9242.rs
@@ -0,0 +1,8 @@
+enum E {
+ X(),
+ Y,
+}
+
+fn main() {
+ let _ = if let E::X() = E::X() { 1 } else { 2 };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.rs b/src/tools/clippy/tests/ui/crashes/ice-96721.rs
new file mode 100644
index 000000000..4b3fb7640
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.rs
@@ -0,0 +1,10 @@
+macro_rules! foo {
+ () => {
+ "bar.rs"
+ };
+}
+
+#[path = foo!()] //~ ERROR malformed `path` attribute
+mod abc {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
new file mode 100644
index 000000000..78c567b8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
@@ -0,0 +1,8 @@
+error: malformed `path` attribute input
+ --> $DIR/ice-96721.rs:7:1
+ |
+LL | #[path = foo!()] //~ ERROR malformed `path` attribute
+ | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs
new file mode 100644
index 000000000..30e4b11ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs
@@ -0,0 +1,19 @@
+#![deny(clippy::all)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
+
+#[allow(dead_code)]
+struct Foo;
+
+impl Iterator for Foo {
+ type Item = ();
+
+ fn next(&mut self) -> Option<()> {
+ let _ = self.len() == 0;
+ unimplemented!()
+ }
+}
+
+impl ExactSizeIterator for Foo {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
new file mode 100644
index 000000000..2f9132929
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
@@ -0,0 +1,16 @@
+#![allow(clippy::comparison_chain)]
+#![deny(clippy::if_same_then_else)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
+
+fn main() {}
+
+pub fn foo(a: i32, b: i32) -> Option<&'static str> {
+ if a == b {
+ None
+ } else if a > b {
+ Some("a pfeil b")
+ } else {
+ None
+ }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/implements-trait.rs b/src/tools/clippy/tests/ui/crashes/implements-trait.rs
new file mode 100644
index 000000000..4502b0147
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/implements-trait.rs
@@ -0,0 +1,5 @@
+#[allow(clippy::needless_borrowed_reference)]
+fn main() {
+ let mut v = Vec::<String>::new();
+ let _ = v.iter_mut().filter(|&ref a| a.is_empty());
+}
diff --git a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs
new file mode 100644
index 000000000..aeb27b5ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs
@@ -0,0 +1,26 @@
+#![deny(clippy::multiple_inherent_impl)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
+
+macro_rules! impl_foo {
+ ($struct:ident) => {
+ impl $struct {
+ fn foo() {}
+ }
+ };
+}
+
+macro_rules! impl_bar {
+ ($struct:ident) => {
+ impl $struct {
+ fn bar() {}
+ }
+ };
+}
+
+struct MyStruct;
+
+impl_foo!(MyStruct);
+impl_bar!(MyStruct);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/issue-825.rs b/src/tools/clippy/tests/ui/crashes/issue-825.rs
new file mode 100644
index 000000000..05696e3d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/issue-825.rs
@@ -0,0 +1,25 @@
+#![allow(warnings)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/825
+
+// this should compile in a reasonable amount of time
+fn rust_type_id(name: &str) {
+ if "bool" == &name[..]
+ || "uint" == &name[..]
+ || "u8" == &name[..]
+ || "u16" == &name[..]
+ || "u32" == &name[..]
+ || "f32" == &name[..]
+ || "f64" == &name[..]
+ || "i8" == &name[..]
+ || "i16" == &name[..]
+ || "i32" == &name[..]
+ || "i64" == &name[..]
+ || "Self" == &name[..]
+ || "str" == &name[..]
+ {
+ unreachable!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
new file mode 100644
index 000000000..bb238c81e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
@@ -0,0 +1,28 @@
+#![allow(dead_code)]
+
+/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596
+pub fn loop_on_block_condition(u: &mut isize) {
+ while { *u < 0 } {
+ *u += 1;
+ }
+}
+
+/// https://github.com/rust-lang/rust-clippy/issues/2584
+fn loop_with_unsafe_condition(ptr: *const u8) {
+ let mut len = 0;
+ while unsafe { *ptr.offset(len) } != 0 {
+ len += 1;
+ }
+}
+
+/// https://github.com/rust-lang/rust-clippy/issues/2710
+static mut RUNNING: bool = true;
+fn loop_on_static_condition() {
+ unsafe {
+ while RUNNING {
+ RUNNING = false;
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
new file mode 100644
index 000000000..94c939665
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
@@ -0,0 +1,18 @@
+#![deny(clippy::match_same_arms)]
+
+/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
+
+const PRICE_OF_SWEETS: u32 = 5;
+const PRICE_OF_KINDNESS: u32 = 0;
+const PRICE_OF_DRINKS: u32 = 5;
+
+pub fn price(thing: &str) -> u32 {
+ match thing {
+ "rolo" => PRICE_OF_SWEETS,
+ "advice" => PRICE_OF_KINDNESS,
+ "juice" => PRICE_OF_DRINKS,
+ _ => panic!(),
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
new file mode 100644
index 000000000..a238e7896
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
@@ -0,0 +1,34 @@
+#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
+#![allow(dead_code)]
+
+// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need
+// the following three lines and the lazy_static crate.
+//
+// #[macro_use]
+// extern crate lazy_static;
+// use std::collections::HashMap;
+
+/// ensure that we don't suggest `is_nan` and `is_null` inside constants
+/// FIXME: once const fn is stable, suggest these functions again in constants
+
+const BAA: *const i32 = 0 as *const i32;
+static mut BAR: *const i32 = BAA;
+static mut FOO: *const i32 = 0 as *const i32;
+static mut BUH: bool = 42.0 < f32::NAN;
+
+#[allow(unused_variables, unused_mut)]
+fn main() {
+ /*
+ lazy_static! {
+ static ref MUT_MAP : HashMap<usize, &'static str> = {
+ let mut m = HashMap::new();
+ m.insert(0, "zero");
+ m
+ };
+ static ref MUT_COUNT : usize = MUT_MAP.len();
+ }
+ assert_eq!(*MUT_COUNT, 1);
+ */
+ // FIXME: don't lint in array length, requires `check_body`
+ //let _ = [""; (42.0 < f32::NAN) as usize];
+}
diff --git a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs
new file mode 100644
index 000000000..4f61c7682
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs
@@ -0,0 +1,7 @@
+#[deny(clippy::all)]
+#[derive(Debug)]
+pub enum Error {
+ Type(&'static str),
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs
new file mode 100644
index 000000000..376ff97ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs
@@ -0,0 +1,20 @@
+#![deny(clippy::needless_lifetimes)]
+#![allow(dead_code)]
+
+trait Foo {}
+
+struct Bar;
+
+struct Baz<'a> {
+ bar: &'a Bar,
+}
+
+impl<'a> Foo for Baz<'a> {}
+
+impl Bar {
+ fn baz<'a>(&'a self) -> impl Foo + 'a {
+ Baz { bar: self }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
new file mode 100644
index 000000000..d68bbe788
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
@@ -0,0 +1,14 @@
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes_impl_trait.rs:15:5
+ |
+LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/needless_lifetimes_impl_trait.rs:1:9
+ |
+LL | #![deny(clippy::needless_lifetimes)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/regressions.rs b/src/tools/clippy/tests/ui/crashes/regressions.rs
new file mode 100644
index 000000000..6f9d98bbf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/regressions.rs
@@ -0,0 +1,11 @@
+#![allow(clippy::blacklisted_name)]
+
+pub fn foo(bar: *const u8) {
+ println!("{:#p}", bar);
+}
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917
+/// <foo
+struct A;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/returns.rs b/src/tools/clippy/tests/ui/crashes/returns.rs
new file mode 100644
index 000000000..8021ed460
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/returns.rs
@@ -0,0 +1,23 @@
+/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
+
+#[deny(warnings)]
+fn cfg_return() -> i32 {
+ #[cfg(unix)]
+ return 1;
+ #[cfg(not(unix))]
+ return 2;
+}
+
+#[deny(warnings)]
+fn cfg_let_and_return() -> i32 {
+ #[cfg(unix)]
+ let x = 1;
+ #[cfg(not(unix))]
+ let x = 2;
+ x
+}
+
+fn main() {
+ cfg_return();
+ cfg_let_and_return();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/shadow.rs b/src/tools/clippy/tests/ui/crashes/shadow.rs
new file mode 100644
index 000000000..843e8ef64
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/shadow.rs
@@ -0,0 +1,6 @@
+fn main() {
+ let x: [i32; {
+ let u = 2;
+ 4
+ }] = [2; { 4 }];
+}
diff --git a/src/tools/clippy/tests/ui/crashes/single-match-else.rs b/src/tools/clippy/tests/ui/crashes/single-match-else.rs
new file mode 100644
index 000000000..1ba7ac082
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/single-match-else.rs
@@ -0,0 +1,11 @@
+#![warn(clippy::single_match_else)]
+
+//! Test for https://github.com/rust-lang/rust-clippy/issues/1588
+
+fn main() {
+ let n = match (42, 43) {
+ (42, n) => n,
+ _ => panic!("typeck error"),
+ };
+ assert_eq!(n, 43);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/third-party/clippy.toml b/src/tools/clippy/tests/ui/crashes/third-party/clippy.toml
new file mode 100644
index 000000000..9f87de20b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/third-party/clippy.toml
@@ -0,0 +1,3 @@
+# this is ignored by Clippy, but allowed for other tools like clippy-service
+[third-party]
+clippy-feature = "nightly"
diff --git a/src/tools/clippy/tests/ui/crashes/third-party/conf_allowlisted.rs b/src/tools/clippy/tests/ui/crashes/third-party/conf_allowlisted.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/third-party/conf_allowlisted.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs
new file mode 100644
index 000000000..60105a821
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs
@@ -0,0 +1,11 @@
+#![feature(trivial_bounds)]
+#![allow(unused, trivial_bounds)]
+
+fn test_trivial_bounds()
+where
+ i32: Iterator,
+{
+ for _ in 2i32 {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs
new file mode 100644
index 000000000..901eb4e50
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs
@@ -0,0 +1,16 @@
+use serde::Deserialize;
+
+/// Tests that we do not lint for unused underscores in a `MacroAttribute`
+/// expansion
+#[deny(clippy::used_underscore_binding)]
+#[derive(Deserialize)]
+struct MacroAttributesTest {
+ _foo: u32,
+}
+
+#[test]
+fn macro_attributes_test() {
+ let _ = MacroAttributesTest { _foo: 0 };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crate_in_macro_def.fixed b/src/tools/clippy/tests/ui/crate_in_macro_def.fixed
new file mode 100644
index 000000000..9fc594be3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_in_macro_def.fixed
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+ #[macro_export]
+ macro_rules! print_message_hygienic {
+ () => {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", $crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/src/tools/clippy/tests/ui/crate_in_macro_def.rs b/src/tools/clippy/tests/ui/crate_in_macro_def.rs
new file mode 100644
index 000000000..ac456108e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_in_macro_def.rs
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+ #[macro_export]
+ macro_rules! print_message_hygienic {
+ () => {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/src/tools/clippy/tests/ui/crate_in_macro_def.stderr b/src/tools/clippy/tests/ui/crate_in_macro_def.stderr
new file mode 100644
index 000000000..9ac5937dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_in_macro_def.stderr
@@ -0,0 +1,10 @@
+error: `crate` references the macro call's crate
+ --> $DIR/crate_in_macro_def.rs:19:28
+ |
+LL | println!("{}", crate::unhygienic::MESSAGE);
+ | ^^^^^ help: to reference the macro definition's crate, use: `$crate`
+ |
+ = note: `-D clippy::crate-in-macro-def` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
new file mode 100644
index 000000000..1b3bcece6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
@@ -0,0 +1,11 @@
+// ignore-macos
+
+#![feature(rustc_attrs)]
+
+#[warn(clippy::main_recursion)]
+#[allow(unconditional_recursion)]
+#[rustc_main]
+fn a() {
+ println!("Hello, World!");
+ a();
+}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr
new file mode 100644
index 000000000..459cf12a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr
@@ -0,0 +1,11 @@
+error: recursing into entrypoint `a`
+ --> $DIR/entrypoint_recursion.rs:10:5
+ |
+LL | a();
+ | ^
+ |
+ = note: `-D clippy::main-recursion` implied by `-D warnings`
+ = help: consider using another function for this recursion
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
new file mode 100644
index 000000000..4a5c597dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
@@ -0,0 +1,33 @@
+// compile-flags: -Clink-arg=-nostartfiles
+// ignore-macos
+// ignore-windows
+
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+use core::panic::PanicInfo;
+use core::sync::atomic::{AtomicUsize, Ordering};
+
+static N: AtomicUsize = AtomicUsize::new(0);
+
+#[warn(clippy::main_recursion)]
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let x = N.load(Ordering::Relaxed);
+ N.store(x + 1, Ordering::Relaxed);
+
+ if x < 3 {
+ main(_argc, _argv);
+ }
+
+ 0
+}
+
+#[allow(clippy::empty_loop)]
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs
new file mode 100644
index 000000000..d3571eaf0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs
@@ -0,0 +1,14 @@
+#![no_std]
+#![feature(lang_items, start, libc)]
+#![crate_type = "lib"]
+
+use core::panic::PanicInfo;
+
+#[warn(clippy::all)]
+fn main() {
+ let mut a = 42;
+ let mut b = 1337;
+
+ a = b;
+ b = a;
+}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr
new file mode 100644
index 000000000..48152d8ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr
@@ -0,0 +1,12 @@
+error: this looks like you are trying to swap `a` and `b`
+ --> $DIR/no_std_swap.rs:12:5
+ |
+LL | / a = b;
+LL | | b = a;
+ | |_________^ help: try: `core::mem::swap(&mut a, &mut b)`
+ |
+ = note: `-D clippy::almost-swapped` implied by `-D warnings`
+ = note: or maybe you should use `core::mem::replace`?
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs
new file mode 100644
index 000000000..89ff66099
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs
@@ -0,0 +1,6 @@
+#[warn(clippy::main_recursion)]
+#[allow(unconditional_recursion)]
+fn main() {
+ println!("Hello, World!");
+ main();
+}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr
new file mode 100644
index 000000000..0a260f9d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr
@@ -0,0 +1,11 @@
+error: recursing into entrypoint `main`
+ --> $DIR/std_main_recursion.rs:5:5
+ |
+LL | main();
+ | ^^^^
+ |
+ = note: `-D clippy::main-recursion` implied by `-D warnings`
+ = help: consider using another function for this recursion
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/create_dir.fixed b/src/tools/clippy/tests/ui/create_dir.fixed
new file mode 100644
index 000000000..8ed53a56a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/create_dir.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+#![allow(unused_must_use)]
+#![warn(clippy::create_dir)]
+
+use std::fs::create_dir_all;
+
+fn create_dir() {}
+
+fn main() {
+ // Should be warned
+ create_dir_all("foo");
+ create_dir_all("bar").unwrap();
+
+ // Shouldn't be warned
+ create_dir();
+ std::fs::create_dir_all("foobar");
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.rs b/src/tools/clippy/tests/ui/create_dir.rs
new file mode 100644
index 000000000..19c8fc24b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/create_dir.rs
@@ -0,0 +1,17 @@
+// run-rustfix
+#![allow(unused_must_use)]
+#![warn(clippy::create_dir)]
+
+use std::fs::create_dir_all;
+
+fn create_dir() {}
+
+fn main() {
+ // Should be warned
+ std::fs::create_dir("foo");
+ std::fs::create_dir("bar").unwrap();
+
+ // Shouldn't be warned
+ create_dir();
+ std::fs::create_dir_all("foobar");
+}
diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr
new file mode 100644
index 000000000..67298fc47
--- /dev/null
+++ b/src/tools/clippy/tests/ui/create_dir.stderr
@@ -0,0 +1,16 @@
+error: calling `std::fs::create_dir` where there may be a better way
+ --> $DIR/create_dir.rs:11:5
+ |
+LL | std::fs::create_dir("foo");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")`
+ |
+ = note: `-D clippy::create-dir` implied by `-D warnings`
+
+error: calling `std::fs::create_dir` where there may be a better way
+ --> $DIR/create_dir.rs:12:5
+ |
+LL | std::fs::create_dir("bar").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs
new file mode 100644
index 000000000..25294e8c7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/dbg_macro.rs
@@ -0,0 +1,60 @@
+// compile-flags: --test
+#![warn(clippy::dbg_macro)]
+
+fn foo(n: u32) -> u32 {
+ if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
+}
+
+fn factorial(n: u32) -> u32 {
+ if dbg!(n <= 1) {
+ dbg!(1)
+ } else {
+ dbg!(n * factorial(n - 1))
+ }
+}
+
+fn main() {
+ dbg!(42);
+ dbg!(dbg!(dbg!(42)));
+ foo(3) + dbg!(factorial(4));
+ dbg!(1, 2, dbg!(3, 4));
+ dbg!(1, 2, 3, 4, 5);
+}
+
+mod issue7274 {
+ trait Thing<'b> {
+ fn foo(&self);
+ }
+
+ macro_rules! define_thing {
+ ($thing:ident, $body:expr) => {
+ impl<'a> Thing<'a> for $thing {
+ fn foo<'b>(&self) {
+ $body
+ }
+ }
+ };
+ }
+
+ struct MyThing;
+ define_thing!(MyThing, {
+ dbg!(2);
+ });
+}
+
+#[test]
+pub fn issue8481() {
+ dbg!(1);
+}
+
+#[cfg(test)]
+fn foo2() {
+ dbg!(1);
+}
+
+#[cfg(test)]
+mod mod1 {
+ fn func() {
+ dbg!(1);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr
new file mode 100644
index 000000000..e6a65b46d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/dbg_macro.stderr
@@ -0,0 +1,146 @@
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:5:22
+ |
+LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::dbg-macro` implied by `-D warnings`
+help: ensure to avoid having uses of it in version control
+ |
+LL | if let Some(n) = n.checked_sub(4) { n } else { n }
+ | ~~~~~~~~~~~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:9:8
+ |
+LL | if dbg!(n <= 1) {
+ | ^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | if n <= 1 {
+ | ~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:10:9
+ |
+LL | dbg!(1)
+ | ^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 1
+ |
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:12:9
+ |
+LL | dbg!(n * factorial(n - 1))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | n * factorial(n - 1)
+ |
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:17:5
+ |
+LL | dbg!(42);
+ | ^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 42;
+ | ~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:18:5
+ |
+LL | dbg!(dbg!(dbg!(42)));
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | dbg!(dbg!(42));
+ | ~~~~~~~~~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:19:14
+ |
+LL | foo(3) + dbg!(factorial(4));
+ | ^^^^^^^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | foo(3) + factorial(4);
+ | ~~~~~~~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:20:5
+ |
+LL | dbg!(1, 2, dbg!(3, 4));
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | (1, 2, dbg!(3, 4));
+ | ~~~~~~~~~~~~~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:21:5
+ |
+LL | dbg!(1, 2, 3, 4, 5);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | (1, 2, 3, 4, 5);
+ | ~~~~~~~~~~~~~~~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:41:9
+ |
+LL | dbg!(2);
+ | ^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 2;
+ | ~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:47:5
+ |
+LL | dbg!(1);
+ | ^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 1;
+ | ~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:52:5
+ |
+LL | dbg!(1);
+ | ^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 1;
+ | ~
+
+error: `dbg!` macro is intended as a debugging tool
+ --> $DIR/dbg_macro.rs:58:9
+ |
+LL | dbg!(1);
+ | ^^^^^^^
+ |
+help: ensure to avoid having uses of it in version control
+ |
+LL | 1;
+ | ~
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs
new file mode 100644
index 000000000..46faa0a7b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs
@@ -0,0 +1,133 @@
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+#![warn(clippy::debug_assert_with_mut_call)]
+#![allow(clippy::redundant_closure_call, clippy::get_first)]
+
+
+struct S;
+
+impl S {
+ fn bool_self_ref(&self) -> bool { false }
+ fn bool_self_mut(&mut self) -> bool { false }
+ fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false }
+ fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false }
+ fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false }
+ fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false }
+
+ fn u32_self_ref(&self) -> u32 { 0 }
+ fn u32_self_mut(&mut self) -> u32 { 0 }
+ fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 }
+ fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 }
+ fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 }
+ fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 }
+}
+
+fn bool_ref(_: &u32) -> bool { false }
+fn bool_mut(_: &mut u32) -> bool { false }
+fn u32_ref(_: &u32) -> u32 { 0 }
+fn u32_mut(_: &mut u32) -> u32 { 0 }
+
+fn func_non_mutable() {
+ debug_assert!(bool_ref(&3));
+ debug_assert!(!bool_ref(&3));
+
+ debug_assert_eq!(0, u32_ref(&3));
+ debug_assert_eq!(u32_ref(&3), 0);
+
+ debug_assert_ne!(1, u32_ref(&3));
+ debug_assert_ne!(u32_ref(&3), 1);
+}
+
+fn func_mutable() {
+ debug_assert!(bool_mut(&mut 3));
+ debug_assert!(!bool_mut(&mut 3));
+
+ debug_assert_eq!(0, u32_mut(&mut 3));
+ debug_assert_eq!(u32_mut(&mut 3), 0);
+
+ debug_assert_ne!(1, u32_mut(&mut 3));
+ debug_assert_ne!(u32_mut(&mut 3), 1);
+}
+
+fn method_non_mutable() {
+ debug_assert!(S.bool_self_ref());
+ debug_assert!(S.bool_self_ref_arg_ref(&3));
+
+ debug_assert_eq!(S.u32_self_ref(), 0);
+ debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0);
+
+ debug_assert_ne!(S.u32_self_ref(), 1);
+ debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1);
+}
+
+fn method_mutable() {
+ debug_assert!(S.bool_self_mut());
+ debug_assert!(!S.bool_self_mut());
+ debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
+ debug_assert!(S.bool_self_mut_arg_ref(&3));
+ debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
+
+ debug_assert_eq!(S.u32_self_mut(), 0);
+ debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
+ debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
+ debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
+
+ debug_assert_ne!(S.u32_self_mut(), 1);
+ debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
+ debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
+ debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
+}
+
+fn misc() {
+ // with variable
+ let mut v: Vec<u32> = vec![1, 2, 3, 4];
+ debug_assert_eq!(v.get(0), Some(&1));
+ debug_assert_ne!(v[0], 2);
+ debug_assert_eq!(v.pop(), Some(1));
+ debug_assert_ne!(Some(3), v.pop());
+
+ let a = &mut 3;
+ debug_assert!(bool_mut(a));
+
+ // nested
+ debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
+
+ // chained
+ debug_assert_eq!(v.pop().unwrap(), 3);
+
+ // format args
+ debug_assert!(bool_ref(&3), "w/o format");
+ debug_assert!(bool_mut(&mut 3), "w/o format");
+ debug_assert!(bool_ref(&3), "{} format", "w/");
+ debug_assert!(bool_mut(&mut 3), "{} format", "w/");
+
+ // sub block
+ let mut x = 42_u32;
+ debug_assert!({
+ bool_mut(&mut x);
+ x > 10
+ });
+
+ // closures
+ debug_assert!((|| {
+ let mut x = 42;
+ bool_mut(&mut x);
+ x > 10
+ })());
+}
+
+async fn debug_await() {
+ debug_assert!(async {
+ true
+ }.await);
+}
+
+fn main() {
+ func_non_mutable();
+ func_mutable();
+ method_non_mutable();
+ method_mutable();
+
+ misc();
+ debug_await();
+}
diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr
new file mode 100644
index 000000000..a2ca71b57
--- /dev/null
+++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr
@@ -0,0 +1,172 @@
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:42:19
+ |
+LL | debug_assert!(bool_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::debug-assert-with-mut-call` implied by `-D warnings`
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:43:20
+ |
+LL | debug_assert!(!bool_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:45:25
+ |
+LL | debug_assert_eq!(0, u32_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:46:22
+ |
+LL | debug_assert_eq!(u32_mut(&mut 3), 0);
+ | ^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:48:25
+ |
+LL | debug_assert_ne!(1, u32_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:49:22
+ |
+LL | debug_assert_ne!(u32_mut(&mut 3), 1);
+ | ^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:64:19
+ |
+LL | debug_assert!(S.bool_self_mut());
+ | ^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:65:20
+ |
+LL | debug_assert!(!S.bool_self_mut());
+ | ^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:66:19
+ |
+LL | debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:67:19
+ |
+LL | debug_assert!(S.bool_self_mut_arg_ref(&3));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:68:19
+ |
+LL | debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:70:22
+ |
+LL | debug_assert_eq!(S.u32_self_mut(), 0);
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:71:22
+ |
+LL | debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:72:22
+ |
+LL | debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:73:22
+ |
+LL | debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:75:22
+ |
+LL | debug_assert_ne!(S.u32_self_mut(), 1);
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:76:22
+ |
+LL | debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:77:22
+ |
+LL | debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:78:22
+ |
+LL | debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:86:22
+ |
+LL | debug_assert_eq!(v.pop(), Some(1));
+ | ^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_ne!`
+ --> $DIR/debug_assert_with_mut_call.rs:87:31
+ |
+LL | debug_assert_ne!(Some(3), v.pop());
+ | ^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:90:19
+ |
+LL | debug_assert!(bool_mut(a));
+ | ^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:93:31
+ |
+LL | debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
+ | ^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert_eq!`
+ --> $DIR/debug_assert_with_mut_call.rs:96:22
+ |
+LL | debug_assert_eq!(v.pop().unwrap(), 3);
+ | ^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:100:19
+ |
+LL | debug_assert!(bool_mut(&mut 3), "w/o format");
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:102:19
+ |
+LL | debug_assert!(bool_mut(&mut 3), "{} format", "w/");
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:107:9
+ |
+LL | bool_mut(&mut x);
+ | ^^^^^^^^^^^^^^^^
+
+error: do not call a function with mutable arguments inside of `debug_assert!`
+ --> $DIR/debug_assert_with_mut_call.rs:114:9
+ |
+LL | bool_mut(&mut x);
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
+
diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.fixed b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed
new file mode 100644
index 000000000..de3914651
--- /dev/null
+++ b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#[warn(clippy::decimal_literal_representation)]
+#[allow(unused_variables)]
+#[rustfmt::skip]
+fn main() {
+ let good = ( // Hex:
+ 127, // 0x7F
+ 256, // 0x100
+ 511, // 0x1FF
+ 2048, // 0x800
+ 4090, // 0xFFA
+ 16_371, // 0x3FF3
+ 61_683, // 0xF0F3
+ 2_131_750_925, // 0x7F0F_F00D
+ );
+ let bad = ( // Hex:
+ 0x8005, // 0x8005
+ 0xFF00, // 0xFF00
+ 0x7F0F_F00F, // 0x7F0F_F00F
+ 0x7FFF_FFFF, // 0x7FFF_FFFF
+ #[allow(overflowing_literals)]
+ 0xF0F0_F0F0, // 0xF0F0_F0F0
+ 0x8005_usize, // 0x8005_usize
+ 0x7F0F_F00F_isize, // 0x7F0F_F00F_isize
+ );
+}
diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.rs b/src/tools/clippy/tests/ui/decimal_literal_representation.rs
new file mode 100644
index 000000000..55d07698e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/decimal_literal_representation.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#[warn(clippy::decimal_literal_representation)]
+#[allow(unused_variables)]
+#[rustfmt::skip]
+fn main() {
+ let good = ( // Hex:
+ 127, // 0x7F
+ 256, // 0x100
+ 511, // 0x1FF
+ 2048, // 0x800
+ 4090, // 0xFFA
+ 16_371, // 0x3FF3
+ 61_683, // 0xF0F3
+ 2_131_750_925, // 0x7F0F_F00D
+ );
+ let bad = ( // Hex:
+ 32_773, // 0x8005
+ 65_280, // 0xFF00
+ 2_131_750_927, // 0x7F0F_F00F
+ 2_147_483_647, // 0x7FFF_FFFF
+ #[allow(overflowing_literals)]
+ 4_042_322_160, // 0xF0F0_F0F0
+ 32_773usize, // 0x8005_usize
+ 2_131_750_927isize, // 0x7F0F_F00F_isize
+ );
+}
diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.stderr b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr
new file mode 100644
index 000000000..8d50c8f83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr
@@ -0,0 +1,46 @@
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:18:9
+ |
+LL | 32_773, // 0x8005
+ | ^^^^^^ help: consider: `0x8005`
+ |
+ = note: `-D clippy::decimal-literal-representation` implied by `-D warnings`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:19:9
+ |
+LL | 65_280, // 0xFF00
+ | ^^^^^^ help: consider: `0xFF00`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:20:9
+ |
+LL | 2_131_750_927, // 0x7F0F_F00F
+ | ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:21:9
+ |
+LL | 2_147_483_647, // 0x7FFF_FFFF
+ | ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:23:9
+ |
+LL | 4_042_322_160, // 0xF0F0_F0F0
+ | ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:24:9
+ |
+LL | 32_773usize, // 0x8005_usize
+ | ^^^^^^^^^^^ help: consider: `0x8005_usize`
+
+error: integer literal has a better hexadecimal representation
+ --> $DIR/decimal_literal_representation.rs:25:9
+ |
+LL | 2_131_750_927isize, // 0x7F0F_F00F_isize
+ | ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs
new file mode 100644
index 000000000..f44518694
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs
@@ -0,0 +1,123 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+enum OptionalCell {
+ Unfrozen(Cell<bool>),
+ Frozen,
+}
+
+// a constant with enums should be linted only when the used variant is unfrozen (#3962).
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+const fn unfrozen_variant() -> OptionalCell {
+ OptionalCell::Unfrozen(Cell::new(false))
+}
+
+const fn frozen_variant() -> OptionalCell {
+ OptionalCell::Frozen
+}
+
+const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
+
+enum NestedInnermost {
+ Unfrozen(AtomicUsize),
+ Frozen,
+}
+
+struct NestedInner {
+ inner: NestedInnermost,
+}
+
+enum NestedOuter {
+ NestedInner(NestedInner),
+ NotNested(usize),
+}
+
+struct NestedOutermost {
+ outer: NestedOuter,
+}
+
+// a constant with enums should be linted according to its value, no matter how structs involve.
+const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
+ outer: NestedOuter::NestedInner(NestedInner {
+ inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
+ }),
+}; //~ ERROR interior mutable
+const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
+ outer: NestedOuter::NestedInner(NestedInner {
+ inner: NestedInnermost::Frozen,
+ }),
+};
+
+trait AssocConsts {
+ // When there's no default value, lint it only according to its type.
+ // Further details are on the corresponding code (`NonCopyConst::check_trait_item`).
+ const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+ const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+
+ // Lint default values accordingly.
+ const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+ const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+}
+
+// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it
+// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'.
+impl AssocConsts for u64 {
+ const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+ const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+ // even if this sets an unfrozen variant, the lint ignores it.
+ const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+}
+
+// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters
+// here are values; and I think substituted generics at definitions won't appear in MIR.
+trait AssocTypes {
+ type ToBeUnfrozen;
+
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+ const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+}
+
+impl AssocTypes for u64 {
+ type ToBeUnfrozen = AtomicUsize;
+
+ const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+ const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
+}
+
+// Use raw pointers since direct generics have a false negative at the type level.
+enum BothOfCellAndGeneric<T> {
+ Unfrozen(Cell<*const T>),
+ Generic(*const T),
+ Frozen(usize),
+}
+
+impl<T> BothOfCellAndGeneric<T> {
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+
+ // This is a false positive. The argument about this is on `is_value_unfrozen_raw`
+ const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+
+ const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
+
+ // This is what is likely to be a false negative when one tries to fix
+ // the `GENERIC_VARIANT` false positive.
+ const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+}
+
+// associated types here is basically the same as the one above.
+trait BothOfCellAndGenericWithAssocType {
+ type AssocType;
+
+ const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
+ BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+ const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr
new file mode 100644
index 000000000..84198d546
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr
@@ -0,0 +1,89 @@
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:12:1
+ |
+LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+ |
+ = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:23:1
+ |
+LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:45:1
+ |
+LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
+ | ^----
+ | |
+ | _make this a static item (maybe with lazy_static)
+ | |
+LL | | outer: NestedOuter::NestedInner(NestedInner {
+LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
+LL | | }),
+LL | | }; //~ ERROR interior mutable
+ | |__^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:59:5
+ |
+LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:60:5
+ |
+LL | const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:63:5
+ |
+LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:89:5
+ |
+LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:101:5
+ |
+LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut...
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:104:5
+ |
+LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:110:5
+ |
+LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:117:5
+ |
+LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
+LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+ | |____________________________________________________________________^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/enums.rs:119:5
+ |
+LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu...
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
new file mode 100644
index 000000000..896596b56
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
@@ -0,0 +1,55 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::fmt::Display;
+use std::sync::atomic::AtomicUsize;
+use std::sync::Once;
+
+const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
+const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+//~^ ERROR interior mutable
+
+macro_rules! declare_const {
+ ($name:ident: $ty:ty = $e:expr) => {
+ const $name: $ty = $e;
+ };
+}
+declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+
+// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
+
+const INTEGER: u8 = 8;
+const STRING: String = String::new();
+const STR: &str = "012345";
+const COW: Cow<str> = Cow::Borrowed("abcdef");
+//^ note: a const item of Cow is used in the `postgres` package.
+
+const NO_ANN: &dyn Display = &70;
+
+static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
+//^ there should be no lints on this line
+
+mod issue_8493 {
+ use std::cell::Cell;
+
+ thread_local! {
+ static _BAR: Cell<i32> = const { Cell::new(0) };
+ }
+
+ macro_rules! issue_8493 {
+ () => {
+ const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
+ static _FOOBAR: () = {
+ thread_local! {
+ static _VAR: Cell<i32> = const { Cell::new(0) };
+ }
+ };
+ };
+ }
+
+ issue_8493!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
new file mode 100644
index 000000000..1fd6d7322
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
@@ -0,0 +1,50 @@
+error: a `const` item should never be interior mutable
+ --> $DIR/others.rs:9:1
+ |
+LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+ |
+ = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+ --> $DIR/others.rs:10:1
+ |
+LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+ --> $DIR/others.rs:11:1
+ |
+LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+ --> $DIR/others.rs:16:9
+ |
+LL | const $name: $ty = $e;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+ | ----------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: a `const` item should never be interior mutable
+ --> $DIR/others.rs:43:13
+ |
+LL | const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | issue_8493!();
+ | ------------- in this macro invocation
+ |
+ = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs
new file mode 100644
index 000000000..256a336db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs
@@ -0,0 +1,150 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+macro_rules! declare_const {
+ ($name:ident: $ty:ty = $e:expr) => {
+ const $name: $ty = $e;
+ };
+}
+
+// a constant whose type is a concrete type should be linted at the definition site.
+trait ConcreteTypes {
+ const ATOMIC: AtomicUsize; //~ ERROR interior mutable
+ const INTEGER: u64;
+ const STRING: String;
+ declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+}
+
+impl ConcreteTypes for u64 {
+ const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+ const INTEGER: u64 = 10;
+ const STRING: String = String::new();
+}
+
+// a helper trait used below
+trait ConstDefault {
+ const DEFAULT: Self;
+}
+
+// a constant whose type is a generic type should be linted at the implementation site.
+trait GenericTypes<T, U> {
+ const TO_REMAIN_GENERIC: T;
+ const TO_BE_CONCRETE: U;
+
+ const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
+ declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
+}
+
+impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
+ const TO_REMAIN_GENERIC: T = T::DEFAULT;
+ const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+}
+
+// a helper type used below
+struct Wrapper<T>(T);
+
+// a constant whose type is an associated type should be linted at the implementation site, too.
+trait AssocTypes {
+ type ToBeFrozen;
+ type ToBeUnfrozen;
+ type ToBeGenericParam;
+
+ const TO_BE_FROZEN: Self::ToBeFrozen;
+ const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
+ const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
+ // to ensure it can handle things when a generic type remains after normalization.
+ const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
+}
+
+impl<T: ConstDefault> AssocTypes for Vec<T> {
+ type ToBeFrozen = u16;
+ type ToBeUnfrozen = AtomicUsize;
+ type ToBeGenericParam = T;
+
+ const TO_BE_FROZEN: Self::ToBeFrozen = 12;
+ const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+ const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+ const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
+}
+
+// a helper trait used below
+trait AssocTypesHelper {
+ type NotToBeBounded;
+ type ToBeBounded;
+
+ const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
+}
+
+// a constant whose type is an assoc type originated from a generic param bounded at the definition
+// site should be linted at there.
+trait AssocTypesFromGenericParam<T>
+where
+ T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ const NOT_BOUNDED: T::NotToBeBounded;
+ const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+}
+
+impl<T> AssocTypesFromGenericParam<T> for u64
+where
+ T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ // an associated type could remain unknown in a trait impl.
+ const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+ const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
+}
+
+// a constant whose type is `Self` should be linted at the implementation site as well.
+// (`Option` requires `Sized` bound.)
+trait SelfType: Sized {
+ const SELF: Self;
+ // this was the one in the original issue (#5050).
+ const WRAPPED_SELF: Option<Self>;
+}
+
+impl SelfType for u64 {
+ const SELF: Self = 16;
+ const WRAPPED_SELF: Option<Self> = Some(20);
+}
+
+impl SelfType for AtomicUsize {
+ // this (interior mutable `Self` const) exists in `parking_lot`.
+ // `const_trait_impl` will replace it in the future, hopefully.
+ const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+ const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+}
+
+// Even though a constant contains a generic type, if it also have an interior mutable type,
+// it should be linted at the definition site.
+trait BothOfCellAndGeneric<T> {
+ // this is a false negative in the current implementation.
+ const DIRECT: Cell<T>;
+ const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+}
+
+impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
+ const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
+ const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
+}
+
+struct Local<T>(T);
+
+// a constant in an inherent impl are essentially the same as a normal const item
+// except there can be a generic or associated type.
+impl<T> Local<T>
+where
+ T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+ const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+ const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
+
+ const GENERIC_TYPE: T = T::DEFAULT;
+
+ const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+ const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr
new file mode 100644
index 000000000..7debe059f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr
@@ -0,0 +1,75 @@
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:15:5
+ |
+LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:9:9
+ |
+LL | const $name: $ty = $e;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+ | ---------------------------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:43:5
+ |
+LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:68:5
+ |
+LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:69:5
+ |
+LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:88:5
+ |
+LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:116:5
+ |
+LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:117:5
+ |
+LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:125:5
+ |
+LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:141:5
+ |
+LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+ --> $DIR/traits.rs:147:5
+ |
+LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs
new file mode 100644
index 000000000..156c88e2e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/def_id_nocore.rs
@@ -0,0 +1,31 @@
+// ignore-windows
+// ignore-macos
+
+#![feature(no_core, lang_items, start)]
+#![no_core]
+#![allow(clippy::missing_safety_doc)]
+
+#[link(name = "c")]
+extern "C" {}
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+#[lang = "freeze"]
+pub unsafe trait Freeze {}
+
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ 0
+}
+
+fn main() {}
+
+struct A;
+
+impl A {
+ pub fn as_ref(self) -> &'static str {
+ "A"
+ }
+}
diff --git a/src/tools/clippy/tests/ui/def_id_nocore.stderr b/src/tools/clippy/tests/ui/def_id_nocore.stderr
new file mode 100644
index 000000000..40d355e9a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/def_id_nocore.stderr
@@ -0,0 +1,11 @@
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
+ --> $DIR/def_id_nocore.rs:28:19
+ |
+LL | pub fn as_ref(self) -> &'static str {
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed
new file mode 100644
index 000000000..f1abfdcd6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+ iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+ // Do lint.
+ let _ = std::iter::empty::<usize>();
+ let _ = std::iter::empty::<HashMap<usize, usize>>();
+ let _foo: std::iter::Empty<usize> = std::iter::empty();
+
+ // Do not lint.
+ let _ = Vec::<usize>::default();
+ let _ = String::default();
+ let _ = Iter::default();
+}
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs
new file mode 100644
index 000000000..2630519c4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+ iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+ // Do lint.
+ let _ = std::iter::Empty::<usize>::default();
+ let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+ let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+
+ // Do not lint.
+ let _ = Vec::<usize>::default();
+ let _ = String::default();
+ let _ = Iter::default();
+}
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr
new file mode 100644
index 000000000..460fc84de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr
@@ -0,0 +1,22 @@
+error: `std::iter::empty()` is the more idiomatic way
+ --> $DIR/default_instead_of_iter_empty.rs:13:13
+ |
+LL | let _ = std::iter::Empty::<usize>::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<usize>()`
+ |
+ = note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings`
+
+error: `std::iter::empty()` is the more idiomatic way
+ --> $DIR/default_instead_of_iter_empty.rs:14:13
+ |
+LL | let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<HashMap<usize, usize>>()`
+
+error: `std::iter::empty()` is the more idiomatic way
+ --> $DIR/default_instead_of_iter_empty.rs:15:41
+ |
+LL | let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
new file mode 100644
index 000000000..a28bff767
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -0,0 +1,177 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12_f64;
+ let x = [1.0_f64, 2.0_f64, 3.0_f64];
+ let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
+ let x = match 1.0_f64 {
+ _ => 1.0_f64,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.0_f64
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1.0_f64 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1.0_f64 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.0_f64);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.0_f64);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1.0_f64 };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1.0_f64 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.0_f64);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest;
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.0_f64);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.0_f64;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
new file mode 100644
index 000000000..b48435cc7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -0,0 +1,177 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12;
+ let x = [1., 2., 3.];
+ let x = if true { (1., 2.) } else { (3., 4.) };
+ let x = match 1. {
+ _ => 1.,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1. };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1. };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1. };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest;
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
new file mode 100644
index 000000000..f8b6c7746
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
@@ -0,0 +1,147 @@
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:21:17
+ |
+LL | let x = 0.12;
+ | ^^^^ help: consider adding suffix: `0.12_f64`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:22:18
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:22:22
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:22:26
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:23:28
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:23:32
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:23:46
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:23:50
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `4.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:24:23
+ |
+LL | let x = match 1. {
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:25:18
+ |
+LL | _ => 1.,
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:43:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:51:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:57:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:69:9
+ |
+LL | 1.
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:75:27
+ |
+LL | let f = || -> _ { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:79:29
+ |
+LL | let f = || -> f64 { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:93:21
+ |
+LL | generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:96:32
+ |
+LL | let x: _ = generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:114:28
+ |
+LL | GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:117:36
+ |
+LL | let _ = GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:135:24
+ |
+LL | GenericEnum::X(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:155:23
+ |
+LL | s.generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:162:21
+ |
+LL | let x = 22.;
+ | ^^^ help: consider adding suffix: `22.0_f64`
+...
+LL | internal_macro!();
+ | ----------------- in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
new file mode 100644
index 000000000..55451cf2f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -0,0 +1,182 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![feature(lint_reasons)]
+#![warn(clippy::default_numeric_fallback)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::let_unit_value
+)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22_i32;
+ let x = [1_i32, 2_i32, 3_i32];
+ let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
+ let x = match 1_i32 {
+ 1_i32 => 1_i32,
+ _ => 2_i32,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1_i32
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1_i32 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1_i32 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1_i32);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1_i32);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1_i32 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1_i32 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1_i32);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest;
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1_i32);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22_i32;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn check_expect_suppression() {
+ #[expect(clippy::default_numeric_fallback)]
+ let x = 21;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
new file mode 100644
index 000000000..62d72f2fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -0,0 +1,182 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![feature(lint_reasons)]
+#![warn(clippy::default_numeric_fallback)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::let_unit_value
+)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22;
+ let x = [1, 2, 3];
+ let x = if true { (1, 2) } else { (3, 4) };
+ let x = match 1 {
+ 1 => 1,
+ _ => 2,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest;
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn check_expect_suppression() {
+ #[expect(clippy::default_numeric_fallback)]
+ let x = 21;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
new file mode 100644
index 000000000..f7c5e724c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
@@ -0,0 +1,159 @@
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:21:17
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:22:18
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:22:21
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:22:24
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:23:28
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:23:31
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:23:44
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:23:47
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:24:23
+ |
+LL | let x = match 1 {
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:25:13
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:25:18
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:26:18
+ |
+LL | _ => 2,
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:43:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:51:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:57:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:69:9
+ |
+LL | 1
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:75:27
+ |
+LL | let f = || -> _ { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:79:29
+ |
+LL | let f = || -> i32 { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:93:21
+ |
+LL | generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:96:32
+ |
+LL | let x: _ = generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:114:28
+ |
+LL | GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:117:36
+ |
+LL | let _ = GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:135:24
+ |
+LL | GenericEnum::X(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:155:23
+ |
+LL | s.generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:162:21
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+...
+LL | internal_macro!();
+ | ----------------- in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/default_trait_access.fixed b/src/tools/clippy/tests/ui/default_trait_access.fixed
new file mode 100644
index 000000000..264dd4efa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_trait_access.fixed
@@ -0,0 +1,99 @@
+// run-rustfix
+
+#![allow(unused_imports, dead_code)]
+#![deny(clippy::default_trait_access)]
+
+use std::default;
+use std::default::Default as D2;
+use std::string;
+
+fn main() {
+ let s1: String = std::string::String::default();
+
+ let s2 = String::default();
+
+ let s3: String = std::string::String::default();
+
+ let s4: String = std::string::String::default();
+
+ let s5 = string::String::default();
+
+ let s6: String = std::string::String::default();
+
+ let s7 = std::string::String::default();
+
+ let s8: String = DefaultFactory::make_t_badly();
+
+ let s9: String = DefaultFactory::make_t_nicely();
+
+ let s10 = DerivedDefault::default();
+
+ let s11: GenericDerivedDefault<String> = GenericDerivedDefault::default();
+
+ let s12 = GenericDerivedDefault::<String>::default();
+
+ let s13 = TupleDerivedDefault::default();
+
+ let s14: TupleDerivedDefault = TupleDerivedDefault::default();
+
+ let s15: ArrayDerivedDefault = ArrayDerivedDefault::default();
+
+ let s16 = ArrayDerivedDefault::default();
+
+ let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default();
+
+ let s18 = TupleStructDerivedDefault::default();
+
+ let s19 = <DerivedDefault as Default>::default();
+
+ let s20 = UpdateSyntax {
+ s: "foo",
+ ..Default::default()
+ };
+
+ println!(
+ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20,
+ );
+}
+
+struct DefaultFactory;
+
+impl DefaultFactory {
+ pub fn make_t_badly<T: Default>() -> T {
+ Default::default()
+ }
+
+ pub fn make_t_nicely<T: Default>() -> T {
+ T::default()
+ }
+}
+
+#[derive(Debug, Default)]
+struct DerivedDefault {
+ pub s: String,
+}
+
+#[derive(Debug, Default)]
+struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
+ pub s: T,
+}
+
+#[derive(Debug, Default)]
+struct TupleDerivedDefault {
+ pub s: (String, String),
+}
+
+#[derive(Debug, Default)]
+struct ArrayDerivedDefault {
+ pub s: [String; 10],
+}
+
+#[derive(Debug, Default)]
+struct TupleStructDerivedDefault(String);
+
+#[derive(Debug, Default)]
+struct UpdateSyntax {
+ pub s: &'static str,
+ pub u: u64,
+}
diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs
new file mode 100644
index 000000000..a0930fab8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_trait_access.rs
@@ -0,0 +1,99 @@
+// run-rustfix
+
+#![allow(unused_imports, dead_code)]
+#![deny(clippy::default_trait_access)]
+
+use std::default;
+use std::default::Default as D2;
+use std::string;
+
+fn main() {
+ let s1: String = Default::default();
+
+ let s2 = String::default();
+
+ let s3: String = D2::default();
+
+ let s4: String = std::default::Default::default();
+
+ let s5 = string::String::default();
+
+ let s6: String = default::Default::default();
+
+ let s7 = std::string::String::default();
+
+ let s8: String = DefaultFactory::make_t_badly();
+
+ let s9: String = DefaultFactory::make_t_nicely();
+
+ let s10 = DerivedDefault::default();
+
+ let s11: GenericDerivedDefault<String> = Default::default();
+
+ let s12 = GenericDerivedDefault::<String>::default();
+
+ let s13 = TupleDerivedDefault::default();
+
+ let s14: TupleDerivedDefault = Default::default();
+
+ let s15: ArrayDerivedDefault = Default::default();
+
+ let s16 = ArrayDerivedDefault::default();
+
+ let s17: TupleStructDerivedDefault = Default::default();
+
+ let s18 = TupleStructDerivedDefault::default();
+
+ let s19 = <DerivedDefault as Default>::default();
+
+ let s20 = UpdateSyntax {
+ s: "foo",
+ ..Default::default()
+ };
+
+ println!(
+ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20,
+ );
+}
+
+struct DefaultFactory;
+
+impl DefaultFactory {
+ pub fn make_t_badly<T: Default>() -> T {
+ Default::default()
+ }
+
+ pub fn make_t_nicely<T: Default>() -> T {
+ T::default()
+ }
+}
+
+#[derive(Debug, Default)]
+struct DerivedDefault {
+ pub s: String,
+}
+
+#[derive(Debug, Default)]
+struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
+ pub s: T,
+}
+
+#[derive(Debug, Default)]
+struct TupleDerivedDefault {
+ pub s: (String, String),
+}
+
+#[derive(Debug, Default)]
+struct ArrayDerivedDefault {
+ pub s: [String; 10],
+}
+
+#[derive(Debug, Default)]
+struct TupleStructDerivedDefault(String);
+
+#[derive(Debug, Default)]
+struct UpdateSyntax {
+ pub s: &'static str,
+ pub u: u64,
+}
diff --git a/src/tools/clippy/tests/ui/default_trait_access.stderr b/src/tools/clippy/tests/ui/default_trait_access.stderr
new file mode 100644
index 000000000..df8a5b94d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_trait_access.stderr
@@ -0,0 +1,56 @@
+error: calling `std::string::String::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:11:22
+ |
+LL | let s1: String = Default::default();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+ |
+note: the lint level is defined here
+ --> $DIR/default_trait_access.rs:4:9
+ |
+LL | #![deny(clippy::default_trait_access)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: calling `std::string::String::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:15:22
+ |
+LL | let s3: String = D2::default();
+ | ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+
+error: calling `std::string::String::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:17:22
+ |
+LL | let s4: String = std::default::Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+
+error: calling `std::string::String::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:21:22
+ |
+LL | let s6: String = default::Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+
+error: calling `GenericDerivedDefault::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:31:46
+ |
+LL | let s11: GenericDerivedDefault<String> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()`
+
+error: calling `TupleDerivedDefault::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:37:36
+ |
+LL | let s14: TupleDerivedDefault = Default::default();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
+
+error: calling `ArrayDerivedDefault::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:39:36
+ |
+LL | let s15: ArrayDerivedDefault = Default::default();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
+
+error: calling `TupleStructDerivedDefault::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:43:42
+ |
+LL | let s17: TupleStructDerivedDefault = Default::default();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/default_union_representation.rs b/src/tools/clippy/tests/ui/default_union_representation.rs
new file mode 100644
index 000000000..93b2d33da
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_union_representation.rs
@@ -0,0 +1,78 @@
+#![feature(transparent_unions)]
+#![warn(clippy::default_union_representation)]
+
+union NoAttribute {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C)]
+union ReprC {
+ a: i32,
+ b: u32,
+}
+
+#[repr(packed)]
+union ReprPacked {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C, packed)]
+union ReprCPacked {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C, align(32))]
+union ReprCAlign {
+ a: i32,
+ b: u32,
+}
+
+#[repr(align(32))]
+union ReprAlign {
+ a: i32,
+ b: u32,
+}
+
+union SingleZST {
+ f0: (),
+}
+union ZSTsAndField1 {
+ f0: u32,
+ f1: (),
+ f2: (),
+ f3: (),
+}
+union ZSTsAndField2 {
+ f0: (),
+ f1: (),
+ f2: u32,
+ f3: (),
+}
+union ZSTAndTwoFields {
+ f0: u32,
+ f1: u64,
+ f2: (),
+}
+
+#[repr(C)]
+union CZSTAndTwoFields {
+ f0: u32,
+ f1: u64,
+ f2: (),
+}
+
+#[repr(transparent)]
+union ReprTransparent {
+ a: i32,
+}
+
+#[repr(transparent)]
+union ReprTransparentZST {
+ a: i32,
+ b: (),
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_union_representation.stderr b/src/tools/clippy/tests/ui/default_union_representation.stderr
new file mode 100644
index 000000000..138884af8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_union_representation.stderr
@@ -0,0 +1,48 @@
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:4:1
+ |
+LL | / union NoAttribute {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::default-union-representation` implied by `-D warnings`
+ = help: consider annotating `NoAttribute` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:16:1
+ |
+LL | / union ReprPacked {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ReprPacked` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:34:1
+ |
+LL | / union ReprAlign {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ReprAlign` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:54:1
+ |
+LL | / union ZSTAndTwoFields {
+LL | | f0: u32,
+LL | | f1: u64,
+LL | | f2: (),
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ZSTAndTwoFields` with `#[repr(C)]` to explicitly specify memory layout
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs
new file mode 100644
index 000000000..07270bd76
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deprecated.rs
@@ -0,0 +1,22 @@
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+#![warn(clippy::should_assert_eq)]
+#![warn(clippy::extend_from_slice)]
+#![warn(clippy::range_step_by_zero)]
+#![warn(clippy::unstable_as_slice)]
+#![warn(clippy::unstable_as_mut_slice)]
+#![warn(clippy::misaligned_transmute)]
+#![warn(clippy::assign_ops)]
+#![warn(clippy::if_let_redundant_pattern_matching)]
+#![warn(clippy::unsafe_vector_initialization)]
+#![warn(clippy::unused_collect)]
+#![warn(clippy::replace_consts)]
+#![warn(clippy::regex_macro)]
+#![warn(clippy::find_map)]
+#![warn(clippy::filter_map)]
+#![warn(clippy::pub_enum_variant_names)]
+#![warn(clippy::wrong_pub_self_convention)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr
new file mode 100644
index 000000000..0e142ac8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deprecated.stderr
@@ -0,0 +1,100 @@
+error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
+ --> $DIR/deprecated.rs:5:9
+ |
+LL | #![warn(clippy::should_assert_eq)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+
+error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
+ --> $DIR/deprecated.rs:6:9
+ |
+LL | #![warn(clippy::extend_from_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
+ --> $DIR/deprecated.rs:7:9
+ |
+LL | #![warn(clippy::range_step_by_zero)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
+ --> $DIR/deprecated.rs:8:9
+ |
+LL | #![warn(clippy::unstable_as_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
+ --> $DIR/deprecated.rs:9:9
+ |
+LL | #![warn(clippy::unstable_as_mut_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
+ --> $DIR/deprecated.rs:10:9
+ |
+LL | #![warn(clippy::misaligned_transmute)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
+ --> $DIR/deprecated.rs:11:9
+ |
+LL | #![warn(clippy::assign_ops)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
+ --> $DIR/deprecated.rs:12:9
+ |
+LL | #![warn(clippy::if_let_redundant_pattern_matching)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
+ --> $DIR/deprecated.rs:13:9
+ |
+LL | #![warn(clippy::unsafe_vector_initialization)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
+ --> $DIR/deprecated.rs:14:9
+ |
+LL | #![warn(clippy::unused_collect)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
+ --> $DIR/deprecated.rs:15:9
+ |
+LL | #![warn(clippy::replace_consts)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
+ --> $DIR/deprecated.rs:16:9
+ |
+LL | #![warn(clippy::regex_macro)]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
+ --> $DIR/deprecated.rs:17:9
+ |
+LL | #![warn(clippy::find_map)]
+ | ^^^^^^^^^^^^^^^^
+
+error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
+ --> $DIR/deprecated.rs:18:9
+ |
+LL | #![warn(clippy::filter_map)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
+ --> $DIR/deprecated.rs:19:9
+ |
+LL | #![warn(clippy::pub_enum_variant_names)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
+ --> $DIR/deprecated.rs:20:9
+ |
+LL | #![warn(clippy::wrong_pub_self_convention)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/deprecated_old.rs b/src/tools/clippy/tests/ui/deprecated_old.rs
new file mode 100644
index 000000000..e89dca4fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deprecated_old.rs
@@ -0,0 +1,5 @@
+#[warn(unstable_as_slice)]
+#[warn(unstable_as_mut_slice)]
+#[warn(misaligned_transmute)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/deprecated_old.stderr b/src/tools/clippy/tests/ui/deprecated_old.stderr
new file mode 100644
index 000000000..8043ab005
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deprecated_old.stderr
@@ -0,0 +1,22 @@
+error: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
+ --> $DIR/deprecated_old.rs:1:8
+ |
+LL | #[warn(unstable_as_slice)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+
+error: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
+ --> $DIR/deprecated_old.rs:2:8
+ |
+LL | #[warn(unstable_as_mut_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
+ --> $DIR/deprecated_old.rs:3:8
+ |
+LL | #[warn(misaligned_transmute)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed
new file mode 100644
index 000000000..2f489deb1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof.fixed
@@ -0,0 +1,68 @@
+// run-rustfix
+#![allow(clippy::return_self_not_must_use)]
+#![warn(clippy::deref_addrof)]
+
+fn get_number() -> usize {
+ 10
+}
+
+fn get_reference(n: &usize) -> &usize {
+ n
+}
+
+#[allow(clippy::double_parens)]
+#[allow(unused_variables, unused_parens)]
+fn main() {
+ let a = 10;
+ let aref = &a;
+
+ let b = a;
+
+ let b = get_number();
+
+ let b = *get_reference(&a);
+
+ let bytes: Vec<usize> = vec![1, 2, 3, 4];
+ let b = bytes[1..2][0];
+
+ //This produces a suggestion of 'let b = (a);' which
+ //will trigger the 'unused_parens' lint
+ let b = (a);
+
+ let b = a;
+
+ #[rustfmt::skip]
+ let b = a;
+
+ let b = &a;
+
+ let b = *aref;
+
+ let _ = unsafe { *core::ptr::addr_of!(a) };
+}
+
+#[rustfmt::skip]
+macro_rules! m {
+ ($visitor: expr) => {
+ $visitor
+ };
+}
+
+#[rustfmt::skip]
+macro_rules! m_mut {
+ ($visitor: expr) => {
+ $visitor
+ };
+}
+
+#[derive(Copy, Clone)]
+pub struct S;
+impl S {
+ pub fn f(&self) -> &Self {
+ m!(self)
+ }
+ #[allow(unused_mut)] // mut will be unused, once the macro is fixed
+ pub fn f_mut(mut self) -> Self {
+ m_mut!(self)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs
new file mode 100644
index 000000000..49f360b9a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof.rs
@@ -0,0 +1,68 @@
+// run-rustfix
+#![allow(clippy::return_self_not_must_use)]
+#![warn(clippy::deref_addrof)]
+
+fn get_number() -> usize {
+ 10
+}
+
+fn get_reference(n: &usize) -> &usize {
+ n
+}
+
+#[allow(clippy::double_parens)]
+#[allow(unused_variables, unused_parens)]
+fn main() {
+ let a = 10;
+ let aref = &a;
+
+ let b = *&a;
+
+ let b = *&get_number();
+
+ let b = *get_reference(&a);
+
+ let bytes: Vec<usize> = vec![1, 2, 3, 4];
+ let b = *&bytes[1..2][0];
+
+ //This produces a suggestion of 'let b = (a);' which
+ //will trigger the 'unused_parens' lint
+ let b = *&(a);
+
+ let b = *(&a);
+
+ #[rustfmt::skip]
+ let b = *((&a));
+
+ let b = *&&a;
+
+ let b = **&aref;
+
+ let _ = unsafe { *core::ptr::addr_of!(a) };
+}
+
+#[rustfmt::skip]
+macro_rules! m {
+ ($visitor: expr) => {
+ *& $visitor
+ };
+}
+
+#[rustfmt::skip]
+macro_rules! m_mut {
+ ($visitor: expr) => {
+ *& mut $visitor
+ };
+}
+
+#[derive(Copy, Clone)]
+pub struct S;
+impl S {
+ pub fn f(&self) -> &Self {
+ m!(self)
+ }
+ #[allow(unused_mut)] // mut will be unused, once the macro is fixed
+ pub fn f_mut(mut self) -> Self {
+ m_mut!(self)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr
new file mode 100644
index 000000000..75371fcdb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof.stderr
@@ -0,0 +1,74 @@
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:19:13
+ |
+LL | let b = *&a;
+ | ^^^ help: try this: `a`
+ |
+ = note: `-D clippy::deref-addrof` implied by `-D warnings`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:21:13
+ |
+LL | let b = *&get_number();
+ | ^^^^^^^^^^^^^^ help: try this: `get_number()`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:26:13
+ |
+LL | let b = *&bytes[1..2][0];
+ | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:30:13
+ |
+LL | let b = *&(a);
+ | ^^^^^ help: try this: `(a)`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:32:13
+ |
+LL | let b = *(&a);
+ | ^^^^^ help: try this: `a`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:35:13
+ |
+LL | let b = *((&a));
+ | ^^^^^^^ help: try this: `a`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:37:13
+ |
+LL | let b = *&&a;
+ | ^^^^ help: try this: `&a`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:39:14
+ |
+LL | let b = **&aref;
+ | ^^^^^^ help: try this: `aref`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:47:9
+ |
+LL | *& $visitor
+ | ^^^^^^^^^^^ help: try this: `$visitor`
+...
+LL | m!(self)
+ | -------- in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof.rs:54:9
+ |
+LL | *& mut $visitor
+ | ^^^^^^^^^^^^^^^ help: try this: `$visitor`
+...
+LL | m_mut!(self)
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `m_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs
new file mode 100644
index 000000000..453194329
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs
@@ -0,0 +1,23 @@
+// This test can't work with run-rustfix because it needs two passes of test+fix
+
+#[warn(clippy::deref_addrof)]
+#[allow(unused_variables, unused_mut)]
+fn main() {
+ let a = 10;
+
+ //This produces a suggestion of 'let b = *&a;' which
+ //will trigger the 'clippy::deref_addrof' lint again
+ let b = **&&a;
+
+ {
+ let mut x = 10;
+ let y = *&mut x;
+ }
+
+ {
+ //This produces a suggestion of 'let y = *&mut x' which
+ //will trigger the 'clippy::deref_addrof' lint again
+ let mut x = 10;
+ let y = **&mut &mut x;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr
new file mode 100644
index 000000000..2c55a4ed6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr
@@ -0,0 +1,22 @@
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof_double_trigger.rs:10:14
+ |
+LL | let b = **&&a;
+ | ^^^^ help: try this: `&a`
+ |
+ = note: `-D clippy::deref-addrof` implied by `-D warnings`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof_double_trigger.rs:14:17
+ |
+LL | let y = *&mut x;
+ | ^^^^^^^ help: try this: `x`
+
+error: immediately dereferencing a reference
+ --> $DIR/deref_addrof_double_trigger.rs:21:18
+ |
+LL | let y = **&mut &mut x;
+ | ^^^^^^^^^^^^ help: try this: `&mut x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/deref_addrof_macro.rs b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
new file mode 100644
index 000000000..dcebd6c6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
@@ -0,0 +1,10 @@
+macro_rules! m {
+ ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] };
+}
+
+#[warn(clippy::deref_addrof)]
+fn f() -> [(i32, &'static str); 3] {
+ *m![1, 2, 3] // should be fine
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.fixed b/src/tools/clippy/tests/ui/deref_by_slicing.fixed
new file mode 100644
index 000000000..257393e56
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_by_slicing.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![warn(clippy::deref_by_slicing)]
+#![allow(clippy::borrow_deref_ref)]
+
+use std::io::Read;
+
+fn main() {
+ let mut vec = vec![0];
+ let _ = &*vec;
+ let _ = &mut *vec;
+
+ let ref_vec = &mut vec;
+ let _ = &**ref_vec;
+ let mut_slice = &mut **ref_vec;
+ let _ = &mut *mut_slice; // Err, re-borrows slice
+
+ let s = String::new();
+ let _ = &*s;
+
+ static S: &[u8] = &[0, 1, 2];
+ let _ = &mut &*S; // Err, re-borrows slice
+
+ let slice: &[u32] = &[0u32, 1u32];
+ let slice_ref = &slice;
+ let _ = *slice_ref; // Err, derefs slice
+
+ let bytes: &[u8] = &[];
+ let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
+}
diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.rs b/src/tools/clippy/tests/ui/deref_by_slicing.rs
new file mode 100644
index 000000000..e288046f9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_by_slicing.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![warn(clippy::deref_by_slicing)]
+#![allow(clippy::borrow_deref_ref)]
+
+use std::io::Read;
+
+fn main() {
+ let mut vec = vec![0];
+ let _ = &vec[..];
+ let _ = &mut vec[..];
+
+ let ref_vec = &mut vec;
+ let _ = &ref_vec[..];
+ let mut_slice = &mut ref_vec[..];
+ let _ = &mut mut_slice[..]; // Err, re-borrows slice
+
+ let s = String::new();
+ let _ = &s[..];
+
+ static S: &[u8] = &[0, 1, 2];
+ let _ = &mut &S[..]; // Err, re-borrows slice
+
+ let slice: &[u32] = &[0u32, 1u32];
+ let slice_ref = &slice;
+ let _ = &slice_ref[..]; // Err, derefs slice
+
+ let bytes: &[u8] = &[];
+ let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
+}
diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.stderr b/src/tools/clippy/tests/ui/deref_by_slicing.stderr
new file mode 100644
index 000000000..8f042ef47
--- /dev/null
+++ b/src/tools/clippy/tests/ui/deref_by_slicing.stderr
@@ -0,0 +1,58 @@
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:10:13
+ |
+LL | let _ = &vec[..];
+ | ^^^^^^^^ help: dereference the original value instead: `&*vec`
+ |
+ = note: `-D clippy::deref-by-slicing` implied by `-D warnings`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:11:13
+ |
+LL | let _ = &mut vec[..];
+ | ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:14:13
+ |
+LL | let _ = &ref_vec[..];
+ | ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:15:21
+ |
+LL | let mut_slice = &mut ref_vec[..];
+ | ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:16:13
+ |
+LL | let _ = &mut mut_slice[..]; // Err, re-borrows slice
+ | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:19:13
+ |
+LL | let _ = &s[..];
+ | ^^^^^^ help: dereference the original value instead: `&*s`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:22:18
+ |
+LL | let _ = &mut &S[..]; // Err, re-borrows slice
+ | ^^^^^^ help: reborrow the original value instead: `&*S`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:26:13
+ |
+LL | let _ = &slice_ref[..]; // Err, derefs slice
+ | ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref`
+
+error: slicing when dereferencing would work
+ --> $DIR/deref_by_slicing.rs:29:13
+ |
+LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
+ | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
new file mode 100644
index 000000000..a64120047
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -0,0 +1,243 @@
+use std::collections::HashMap;
+
+struct FooDefault<'a> {
+ a: bool,
+ b: i32,
+ c: u64,
+ d: Vec<i32>,
+ e: FooND1,
+ f: FooND2,
+ g: HashMap<i32, i32>,
+ h: (i32, Vec<i32>),
+ i: [Vec<i32>; 3],
+ j: [i32; 5],
+ k: Option<i32>,
+ l: &'a [i32],
+}
+
+impl std::default::Default for FooDefault<'_> {
+ fn default() -> Self {
+ Self {
+ a: false,
+ b: 0,
+ c: 0u64,
+ d: vec![],
+ e: Default::default(),
+ f: FooND2::default(),
+ g: HashMap::new(),
+ h: (0, vec![]),
+ i: [vec![], vec![], vec![]],
+ j: [0; 5],
+ k: None,
+ l: &[],
+ }
+ }
+}
+
+struct TupleDefault(bool, i32, u64);
+
+impl std::default::Default for TupleDefault {
+ fn default() -> Self {
+ Self(false, 0, 0u64)
+ }
+}
+
+struct FooND1 {
+ a: bool,
+}
+
+impl std::default::Default for FooND1 {
+ fn default() -> Self {
+ Self { a: true }
+ }
+}
+
+struct FooND2 {
+ a: i32,
+}
+
+impl std::default::Default for FooND2 {
+ fn default() -> Self {
+ Self { a: 5 }
+ }
+}
+
+struct FooNDNew {
+ a: bool,
+}
+
+impl FooNDNew {
+ fn new() -> Self {
+ Self { a: true }
+ }
+}
+
+impl Default for FooNDNew {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+struct FooNDVec(Vec<i32>);
+
+impl Default for FooNDVec {
+ fn default() -> Self {
+ Self(vec![5, 12])
+ }
+}
+
+struct StrDefault<'a>(&'a str);
+
+impl Default for StrDefault<'_> {
+ fn default() -> Self {
+ Self("")
+ }
+}
+
+#[derive(Default)]
+struct AlreadyDerived(i32, bool);
+
+macro_rules! mac {
+ () => {
+ 0
+ };
+ ($e:expr) => {
+ struct X(u32);
+ impl Default for X {
+ fn default() -> Self {
+ Self($e)
+ }
+ }
+ };
+}
+
+mac!(0);
+
+struct Y(u32);
+impl Default for Y {
+ fn default() -> Self {
+ Self(mac!())
+ }
+}
+
+struct RustIssue26925<T> {
+ a: Option<T>,
+}
+
+// We should watch out for cases where a manual impl is needed because a
+// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
+// For example, a struct with Option<T> does not require T: Default, but a derive adds
+// that type bound anyways. So until #26925 get fixed we should disable lint
+// for the following case
+impl<T> Default for RustIssue26925<T> {
+ fn default() -> Self {
+ Self { a: None }
+ }
+}
+
+struct SpecializedImpl<A, B> {
+ a: A,
+ b: B,
+}
+
+impl<T: Default> Default for SpecializedImpl<T, T> {
+ fn default() -> Self {
+ Self {
+ a: T::default(),
+ b: T::default(),
+ }
+ }
+}
+
+struct WithoutSelfCurly {
+ a: bool,
+}
+
+impl Default for WithoutSelfCurly {
+ fn default() -> Self {
+ WithoutSelfCurly { a: false }
+ }
+}
+
+struct WithoutSelfParan(bool);
+
+impl Default for WithoutSelfParan {
+ fn default() -> Self {
+ WithoutSelfParan(false)
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7655
+
+pub struct SpecializedImpl2<T> {
+ v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+ fn default() -> Self {
+ Self { v: Vec::new() }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7654
+
+pub struct Color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+/// `#000000`
+impl Default for Color {
+ fn default() -> Self {
+ Color { r: 0, g: 0, b: 0 }
+ }
+}
+
+pub struct Color2 {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+impl Default for Color2 {
+ /// `#000000`
+ fn default() -> Self {
+ Self { r: 0, g: 0, b: 0 }
+ }
+}
+
+pub struct RepeatDefault1 {
+ a: [i8; 32],
+}
+
+impl Default for RepeatDefault1 {
+ fn default() -> Self {
+ RepeatDefault1 { a: [0; 32] }
+ }
+}
+
+pub struct RepeatDefault2 {
+ a: [i8; 33],
+}
+
+impl Default for RepeatDefault2 {
+ fn default() -> Self {
+ RepeatDefault2 { a: [0; 33] }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7753
+
+pub enum IntOrString {
+ Int(i32),
+ String(String),
+}
+
+impl Default for IntOrString {
+ fn default() -> Self {
+ IntOrString::Int(0)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr
new file mode 100644
index 000000000..49fb471a2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls.stderr
@@ -0,0 +1,89 @@
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:18:1
+ |
+LL | / impl std::default::Default for FooDefault<'_> {
+LL | | fn default() -> Self {
+LL | | Self {
+LL | | a: false,
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::derivable-impls` implied by `-D warnings`
+ = help: try annotating `FooDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:39:1
+ |
+LL | / impl std::default::Default for TupleDefault {
+LL | | fn default() -> Self {
+LL | | Self(false, 0, 0u64)
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `TupleDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:91:1
+ |
+LL | / impl Default for StrDefault<'_> {
+LL | | fn default() -> Self {
+LL | | Self("")
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `StrDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:117:1
+ |
+LL | / impl Default for Y {
+LL | | fn default() -> Self {
+LL | | Self(mac!())
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `Y` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:156:1
+ |
+LL | / impl Default for WithoutSelfCurly {
+LL | | fn default() -> Self {
+LL | | WithoutSelfCurly { a: false }
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:164:1
+ |
+LL | / impl Default for WithoutSelfParan {
+LL | | fn default() -> Self {
+LL | | WithoutSelfParan(false)
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `WithoutSelfParan` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:214:1
+ |
+LL | / impl Default for RepeatDefault1 {
+LL | | fn default() -> Self {
+LL | | RepeatDefault1 { a: [0; 32] }
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: try annotating `RepeatDefault1` with `#[derive(Default)]`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs
new file mode 100644
index 000000000..b276c384c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive.rs
@@ -0,0 +1,89 @@
+#![allow(dead_code)]
+#![warn(clippy::expl_impl_clone_on_copy)]
+
+
+#[derive(Copy)]
+struct Qux;
+
+impl Clone for Qux {
+ fn clone(&self) -> Self {
+ Qux
+ }
+}
+
+// looks like unions don't support deriving Clone for now
+#[derive(Copy)]
+union Union {
+ a: u8,
+}
+
+impl Clone for Union {
+ fn clone(&self) -> Self {
+ Union { a: 42 }
+ }
+}
+
+// See #666
+#[derive(Copy)]
+struct Lt<'a> {
+ a: &'a u8,
+}
+
+impl<'a> Clone for Lt<'a> {
+ fn clone(&self) -> Self {
+ unimplemented!()
+ }
+}
+
+#[derive(Copy)]
+struct BigArray {
+ a: [u8; 65],
+}
+
+impl Clone for BigArray {
+ fn clone(&self) -> Self {
+ unimplemented!()
+ }
+}
+
+#[derive(Copy)]
+struct FnPtr {
+ a: fn() -> !,
+}
+
+impl Clone for FnPtr {
+ fn clone(&self) -> Self {
+ unimplemented!()
+ }
+}
+
+// Ok, Clone trait impl doesn't have constrained generics.
+#[derive(Copy)]
+struct Generic<T> {
+ a: T,
+}
+
+impl<T> Clone for Generic<T> {
+ fn clone(&self) -> Self {
+ unimplemented!()
+ }
+}
+
+#[derive(Copy)]
+struct Generic2<T>(T);
+impl<T: Clone> Clone for Generic2<T> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+
+// Ok, Clone trait impl doesn't have constrained generics.
+#[derive(Copy)]
+struct GenericRef<'a, T, U>(T, &'a U);
+impl<T: Clone, U> Clone for GenericRef<'_, T, U> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone(), self.1)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr
new file mode 100644
index 000000000..82a70ceec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive.stderr
@@ -0,0 +1,103 @@
+error: you are implementing `Clone` explicitly on a `Copy` type
+ --> $DIR/derive.rs:8:1
+ |
+LL | / impl Clone for Qux {
+LL | | fn clone(&self) -> Self {
+LL | | Qux
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings`
+note: consider deriving `Clone` or removing `Copy`
+ --> $DIR/derive.rs:8:1
+ |
+LL | / impl Clone for Qux {
+LL | | fn clone(&self) -> Self {
+LL | | Qux
+LL | | }
+LL | | }
+ | |_^
+
+error: you are implementing `Clone` explicitly on a `Copy` type
+ --> $DIR/derive.rs:32:1
+ |
+LL | / impl<'a> Clone for Lt<'a> {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+ |
+note: consider deriving `Clone` or removing `Copy`
+ --> $DIR/derive.rs:32:1
+ |
+LL | / impl<'a> Clone for Lt<'a> {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+
+error: you are implementing `Clone` explicitly on a `Copy` type
+ --> $DIR/derive.rs:43:1
+ |
+LL | / impl Clone for BigArray {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+ |
+note: consider deriving `Clone` or removing `Copy`
+ --> $DIR/derive.rs:43:1
+ |
+LL | / impl Clone for BigArray {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+
+error: you are implementing `Clone` explicitly on a `Copy` type
+ --> $DIR/derive.rs:54:1
+ |
+LL | / impl Clone for FnPtr {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+ |
+note: consider deriving `Clone` or removing `Copy`
+ --> $DIR/derive.rs:54:1
+ |
+LL | / impl Clone for FnPtr {
+LL | | fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+LL | | }
+ | |_^
+
+error: you are implementing `Clone` explicitly on a `Copy` type
+ --> $DIR/derive.rs:74:1
+ |
+LL | / impl<T: Clone> Clone for Generic2<T> {
+LL | | fn clone(&self) -> Self {
+LL | | Self(self.0.clone())
+LL | | }
+LL | | }
+ | |_^
+ |
+note: consider deriving `Clone` or removing `Copy`
+ --> $DIR/derive.rs:74:1
+ |
+LL | / impl<T: Clone> Clone for Generic2<T> {
+LL | | fn clone(&self) -> Self {
+LL | | Self(self.0.clone())
+LL | | }
+LL | | }
+ | |_^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs
new file mode 100644
index 000000000..813ddc566
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs
@@ -0,0 +1,56 @@
+#![allow(clippy::derive_partial_eq_without_eq)]
+
+#[derive(PartialEq, Hash)]
+struct Foo;
+
+impl PartialEq<u64> for Foo {
+ fn eq(&self, _: &u64) -> bool {
+ true
+ }
+}
+
+#[derive(Hash)]
+struct Bar;
+
+impl PartialEq for Bar {
+ fn eq(&self, _: &Bar) -> bool {
+ true
+ }
+}
+
+#[derive(Hash)]
+struct Baz;
+
+impl PartialEq<Baz> for Baz {
+ fn eq(&self, _: &Baz) -> bool {
+ true
+ }
+}
+
+#[derive(PartialEq)]
+struct Bah;
+
+impl std::hash::Hash for Bah {
+ fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
+}
+
+#[derive(PartialEq)]
+struct Foo2;
+
+trait Hash {}
+
+// We don't want to lint on user-defined traits called `Hash`
+impl Hash for Foo2 {}
+
+mod use_hash {
+ use std::hash::{Hash, Hasher};
+
+ #[derive(PartialEq)]
+ struct Foo3;
+
+ impl Hash for Foo3 {
+ fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr
new file mode 100644
index 000000000..2a4abb0c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr
@@ -0,0 +1,59 @@
+error: you are deriving `Hash` but have implemented `PartialEq` explicitly
+ --> $DIR/derive_hash_xor_eq.rs:12:10
+ |
+LL | #[derive(Hash)]
+ | ^^^^
+ |
+ = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default
+note: `PartialEq` implemented here
+ --> $DIR/derive_hash_xor_eq.rs:15:1
+ |
+LL | impl PartialEq for Bar {
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `Hash` but have implemented `PartialEq` explicitly
+ --> $DIR/derive_hash_xor_eq.rs:21:10
+ |
+LL | #[derive(Hash)]
+ | ^^^^
+ |
+note: `PartialEq` implemented here
+ --> $DIR/derive_hash_xor_eq.rs:24:1
+ |
+LL | impl PartialEq<Baz> for Baz {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Hash` explicitly but have derived `PartialEq`
+ --> $DIR/derive_hash_xor_eq.rs:33:1
+ |
+LL | / impl std::hash::Hash for Bah {
+LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
+LL | | }
+ | |_^
+ |
+note: `PartialEq` implemented here
+ --> $DIR/derive_hash_xor_eq.rs:30:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^
+ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Hash` explicitly but have derived `PartialEq`
+ --> $DIR/derive_hash_xor_eq.rs:51:5
+ |
+LL | / impl Hash for Foo3 {
+LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
+LL | | }
+ | |_____^
+ |
+note: `PartialEq` implemented here
+ --> $DIR/derive_hash_xor_eq.rs:48:14
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^
+ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
new file mode 100644
index 000000000..6f12d36d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
@@ -0,0 +1,69 @@
+#![warn(clippy::derive_ord_xor_partial_ord)]
+#![allow(clippy::unnecessary_wraps)]
+
+use std::cmp::Ordering;
+
+#[derive(PartialOrd, Ord, PartialEq, Eq)]
+struct DeriveBoth;
+
+impl PartialEq<u64> for DeriveBoth {
+ fn eq(&self, _: &u64) -> bool {
+ true
+ }
+}
+
+impl PartialOrd<u64> for DeriveBoth {
+ fn partial_cmp(&self, _: &u64) -> Option<Ordering> {
+ Some(Ordering::Equal)
+ }
+}
+
+#[derive(Ord, PartialEq, Eq)]
+struct DeriveOrd;
+
+impl PartialOrd for DeriveOrd {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(other.cmp(self))
+ }
+}
+
+#[derive(Ord, PartialEq, Eq)]
+struct DeriveOrdWithExplicitTypeVariable;
+
+impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(other.cmp(self))
+ }
+}
+
+#[derive(PartialOrd, PartialEq, Eq)]
+struct DerivePartialOrd;
+
+impl std::cmp::Ord for DerivePartialOrd {
+ fn cmp(&self, other: &Self) -> Ordering {
+ Ordering::Less
+ }
+}
+
+#[derive(PartialOrd, PartialEq, Eq)]
+struct ImplUserOrd;
+
+trait Ord {}
+
+// We don't want to lint on user-defined traits called `Ord`
+impl Ord for ImplUserOrd {}
+
+mod use_ord {
+ use std::cmp::{Ord, Ordering};
+
+ #[derive(PartialOrd, PartialEq, Eq)]
+ struct DerivePartialOrdInUseOrd;
+
+ impl Ord for DerivePartialOrdInUseOrd {
+ fn cmp(&self, other: &Self) -> Ordering {
+ Ordering::Less
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr
new file mode 100644
index 000000000..baf8341ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr
@@ -0,0 +1,63 @@
+error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
+ --> $DIR/derive_ord_xor_partial_ord.rs:21:10
+ |
+LL | #[derive(Ord, PartialEq, Eq)]
+ | ^^^
+ |
+ = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings`
+note: `PartialOrd` implemented here
+ --> $DIR/derive_ord_xor_partial_ord.rs:24:1
+ |
+LL | impl PartialOrd for DeriveOrd {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
+ --> $DIR/derive_ord_xor_partial_ord.rs:30:10
+ |
+LL | #[derive(Ord, PartialEq, Eq)]
+ | ^^^
+ |
+note: `PartialOrd` implemented here
+ --> $DIR/derive_ord_xor_partial_ord.rs:33:1
+ |
+LL | impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Ord` explicitly but have derived `PartialOrd`
+ --> $DIR/derive_ord_xor_partial_ord.rs:42:1
+ |
+LL | / impl std::cmp::Ord for DerivePartialOrd {
+LL | | fn cmp(&self, other: &Self) -> Ordering {
+LL | | Ordering::Less
+LL | | }
+LL | | }
+ | |_^
+ |
+note: `PartialOrd` implemented here
+ --> $DIR/derive_ord_xor_partial_ord.rs:39:10
+ |
+LL | #[derive(PartialOrd, PartialEq, Eq)]
+ | ^^^^^^^^^^
+ = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Ord` explicitly but have derived `PartialOrd`
+ --> $DIR/derive_ord_xor_partial_ord.rs:62:5
+ |
+LL | / impl Ord for DerivePartialOrdInUseOrd {
+LL | | fn cmp(&self, other: &Self) -> Ordering {
+LL | | Ordering::Less
+LL | | }
+LL | | }
+ | |_____^
+ |
+note: `PartialOrd` implemented here
+ --> $DIR/derive_ord_xor_partial_ord.rs:59:14
+ |
+LL | #[derive(PartialOrd, PartialEq, Eq)]
+ | ^^^^^^^^^^
+ = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
new file mode 100644
index 000000000..bbbe46759
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
@@ -0,0 +1,126 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::derive_partial_eq_without_eq)]
+
+// Don't warn on structs that aren't PartialEq
+pub struct NotPartialEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq can be derived but is missing
+#[derive(Debug, PartialEq, Eq)]
+pub struct MissingEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq is derived
+#[derive(PartialEq, Eq)]
+pub struct NotMissingEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq is manually implemented
+#[derive(PartialEq)]
+pub struct ManualEqImpl {
+ foo: u32,
+ bar: String,
+}
+
+impl Eq for ManualEqImpl {}
+
+// Cannot be Eq because f32 isn't Eq
+#[derive(PartialEq)]
+pub struct CannotBeEq {
+ foo: u32,
+ bar: f32,
+}
+
+// Don't warn if PartialEq is manually implemented
+pub struct ManualPartialEqImpl {
+ foo: u32,
+ bar: String,
+}
+
+impl PartialEq for ManualPartialEqImpl {
+ fn eq(&self, other: &Self) -> bool {
+ self.foo == other.foo && self.bar == other.bar
+ }
+}
+
+// Generic fields should be properly checked for Eq-ness
+#[derive(PartialEq, Eq)]
+pub struct GenericNotEq<T: Eq, U: PartialEq> {
+ foo: T,
+ bar: U,
+}
+
+#[derive(PartialEq, Eq)]
+pub struct GenericEq<T: Eq, U: Eq> {
+ foo: T,
+ bar: U,
+}
+
+#[derive(PartialEq, Eq)]
+pub struct TupleStruct(u32);
+
+#[derive(PartialEq, Eq)]
+pub struct GenericTupleStruct<T: Eq>(T);
+
+#[derive(PartialEq)]
+pub struct TupleStructNotEq(f32);
+
+#[derive(PartialEq, Eq)]
+pub enum Enum {
+ Foo(u32),
+ Bar { a: String, b: () },
+}
+
+#[derive(PartialEq, Eq)]
+pub enum GenericEnum<T: Eq, U: Eq, V: Eq> {
+ Foo(T),
+ Bar { a: U, b: V },
+}
+
+#[derive(PartialEq)]
+pub enum EnumNotEq {
+ Foo(u32),
+ Bar { a: String, b: f32 },
+}
+
+// Ensure that rustfix works properly when `PartialEq` has other derives on either side
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct RustFixWithOtherDerives;
+
+#[derive(PartialEq, Eq)]
+pub struct Generic<T>(T);
+
+#[derive(PartialEq, Eq)]
+pub struct GenericPhantom<T>(core::marker::PhantomData<T>);
+
+mod _hidden {
+ #[derive(PartialEq, Eq)]
+ pub struct Reexported;
+
+ #[derive(PartialEq, Eq)]
+ pub struct InPubFn;
+
+ #[derive(PartialEq)]
+ pub(crate) struct PubCrate;
+
+ #[derive(PartialEq)]
+ pub(super) struct PubSuper;
+}
+
+pub use _hidden::Reexported;
+pub fn _from_mod() -> _hidden::InPubFn {
+ _hidden::InPubFn
+}
+
+#[derive(PartialEq)]
+struct InternalTy;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
new file mode 100644
index 000000000..88d6fbd1a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
@@ -0,0 +1,126 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::derive_partial_eq_without_eq)]
+
+// Don't warn on structs that aren't PartialEq
+pub struct NotPartialEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq can be derived but is missing
+#[derive(Debug, PartialEq)]
+pub struct MissingEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq is derived
+#[derive(PartialEq, Eq)]
+pub struct NotMissingEq {
+ foo: u32,
+ bar: String,
+}
+
+// Eq is manually implemented
+#[derive(PartialEq)]
+pub struct ManualEqImpl {
+ foo: u32,
+ bar: String,
+}
+
+impl Eq for ManualEqImpl {}
+
+// Cannot be Eq because f32 isn't Eq
+#[derive(PartialEq)]
+pub struct CannotBeEq {
+ foo: u32,
+ bar: f32,
+}
+
+// Don't warn if PartialEq is manually implemented
+pub struct ManualPartialEqImpl {
+ foo: u32,
+ bar: String,
+}
+
+impl PartialEq for ManualPartialEqImpl {
+ fn eq(&self, other: &Self) -> bool {
+ self.foo == other.foo && self.bar == other.bar
+ }
+}
+
+// Generic fields should be properly checked for Eq-ness
+#[derive(PartialEq)]
+pub struct GenericNotEq<T: Eq, U: PartialEq> {
+ foo: T,
+ bar: U,
+}
+
+#[derive(PartialEq)]
+pub struct GenericEq<T: Eq, U: Eq> {
+ foo: T,
+ bar: U,
+}
+
+#[derive(PartialEq)]
+pub struct TupleStruct(u32);
+
+#[derive(PartialEq)]
+pub struct GenericTupleStruct<T: Eq>(T);
+
+#[derive(PartialEq)]
+pub struct TupleStructNotEq(f32);
+
+#[derive(PartialEq)]
+pub enum Enum {
+ Foo(u32),
+ Bar { a: String, b: () },
+}
+
+#[derive(PartialEq)]
+pub enum GenericEnum<T: Eq, U: Eq, V: Eq> {
+ Foo(T),
+ Bar { a: U, b: V },
+}
+
+#[derive(PartialEq)]
+pub enum EnumNotEq {
+ Foo(u32),
+ Bar { a: String, b: f32 },
+}
+
+// Ensure that rustfix works properly when `PartialEq` has other derives on either side
+#[derive(Debug, PartialEq, Clone)]
+pub struct RustFixWithOtherDerives;
+
+#[derive(PartialEq)]
+pub struct Generic<T>(T);
+
+#[derive(PartialEq, Eq)]
+pub struct GenericPhantom<T>(core::marker::PhantomData<T>);
+
+mod _hidden {
+ #[derive(PartialEq)]
+ pub struct Reexported;
+
+ #[derive(PartialEq)]
+ pub struct InPubFn;
+
+ #[derive(PartialEq)]
+ pub(crate) struct PubCrate;
+
+ #[derive(PartialEq)]
+ pub(super) struct PubSuper;
+}
+
+pub use _hidden::Reexported;
+pub fn _from_mod() -> _hidden::InPubFn {
+ _hidden::InPubFn
+}
+
+#[derive(PartialEq)]
+struct InternalTy;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr
new file mode 100644
index 000000000..794c5dab8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr
@@ -0,0 +1,70 @@
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:13:17
+ |
+LL | #[derive(Debug, PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+ |
+ = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:55:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:61:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:67:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:70:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:76:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:82:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:95:17
+ |
+LL | #[derive(Debug, PartialEq, Clone)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:98:10
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:105:14
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: you are deriving `PartialEq` and can implement `Eq`
+ --> $DIR/derive_partial_eq_without_eq.rs:108:14
+ |
+LL | #[derive(PartialEq)]
+ | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/disallowed_script_idents.rs b/src/tools/clippy/tests/ui/disallowed_script_idents.rs
new file mode 100644
index 000000000..cfdda3597
--- /dev/null
+++ b/src/tools/clippy/tests/ui/disallowed_script_idents.rs
@@ -0,0 +1,10 @@
+#![deny(clippy::disallowed_script_idents)]
+#![allow(dead_code)]
+
+fn main() {
+ let counter = 10; // OK, latin is allowed.
+ let zähler = 10; // OK, it's still latin.
+
+ let счётчик = 10; // Cyrillic is not allowed by default.
+ let カウンタ = 10; // Same for japanese.
+}
diff --git a/src/tools/clippy/tests/ui/disallowed_script_idents.stderr b/src/tools/clippy/tests/ui/disallowed_script_idents.stderr
new file mode 100644
index 000000000..cc84dc1d4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/disallowed_script_idents.stderr
@@ -0,0 +1,20 @@
+error: identifier `счётчик` has a Unicode script that is not allowed by configuration: Cyrillic
+ --> $DIR/disallowed_script_idents.rs:8:9
+ |
+LL | let счётчик = 10; // Cyrillic is not allowed by default.
+ | ^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/disallowed_script_idents.rs:1:9
+ |
+LL | #![deny(clippy::disallowed_script_idents)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana
+ --> $DIR/disallowed_script_idents.rs:9:9
+ |
+LL | let カウンタ = 10; // Same for japanese.
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
new file mode 100644
index 000000000..e27f9fea7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
@@ -0,0 +1,41 @@
+#![warn(clippy::diverging_sub_expression)]
+#![allow(clippy::match_same_arms, clippy::logic_bug)]
+#[allow(clippy::empty_loop)]
+fn diverge() -> ! {
+ loop {}
+}
+
+struct A;
+
+impl A {
+ fn foo(&self) -> ! {
+ diverge()
+ }
+}
+
+#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
+fn main() {
+ let b = true;
+ b || diverge();
+ b || A.foo();
+}
+
+#[allow(dead_code, unused_variables)]
+fn foobar() {
+ loop {
+ let x = match 5 {
+ 4 => return,
+ 5 => continue,
+ 6 => true || return,
+ 7 => true || continue,
+ 8 => break,
+ 9 => diverge(),
+ 3 => true || diverge(),
+ 10 => match 42 {
+ 99 => return,
+ _ => true || panic!("boo"),
+ },
+ _ => true || break,
+ };
+ }
+}
diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr
new file mode 100644
index 000000000..9c91d9357
--- /dev/null
+++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr
@@ -0,0 +1,48 @@
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:19:10
+ |
+LL | b || diverge();
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:20:10
+ |
+LL | b || A.foo();
+ | ^^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:29:26
+ |
+LL | 6 => true || return,
+ | ^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:30:26
+ |
+LL | 7 => true || continue,
+ | ^^^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:33:26
+ |
+LL | 3 => true || diverge(),
+ | ^^^^^^^^^
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:36:30
+ |
+LL | _ => true || panic!("boo"),
+ | ^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: sub-expression diverges
+ --> $DIR/diverging_sub_expression.rs:38:26
+ |
+LL | _ => true || break,
+ | ^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
new file mode 100644
index 000000000..747801b40
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -0,0 +1,215 @@
+// run-rustfix
+//! This file tests for the `DOC_MARKDOWN` lint.
+
+#![allow(dead_code, incomplete_features)]
+#![warn(clippy::doc_markdown)]
+#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
+#![rustfmt::skip]
+
+/// The `foo_bar` function does _nothing_. See also `foo::bar`. (note the dot there)
+/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not `Foo::some_fun`
+/// which should be reported only once despite being __doubly bad__.
+/// Here be `::a::global:path`, and _`::another::global::path`_. :: is not a path though.
+/// Import an item from `::awesome::global::blob::` (Intended postfix)
+/// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+/// That's not code ~`NotInCodeBlock`~.
+/// `be_sure_we_got_to_the_end_of_it`
+fn foo_bar() {
+}
+
+/// That one tests multiline ticks.
+/// ```rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ```
+///
+/// ~~~rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ~~~
+/// `be_sure_we_got_to_the_end_of_it`
+fn multiline_codeblock() {
+}
+
+/// This _is a test for
+/// multiline
+/// emphasis_.
+/// `be_sure_we_got_to_the_end_of_it`
+fn test_emphasis() {
+}
+
+/// This tests units. See also #835.
+/// kiB MiB GiB TiB PiB EiB
+/// kib Mib Gib Tib Pib Eib
+/// kB MB GB TB PB EB
+/// kb Mb Gb Tb Pb Eb
+/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
+/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
+/// 32kB 32MB 32GB 32TB 32PB 32EB
+/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
+/// NaN
+/// `be_sure_we_got_to_the_end_of_it`
+fn test_units() {
+}
+
+/// This tests allowed identifiers.
+/// KiB MiB GiB TiB PiB EiB
+/// DirectX
+/// ECMAScript
+/// GPLv2 GPLv3
+/// GitHub GitLab
+/// IPv4 IPv6
+/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// NaN NaNs
+/// OAuth GraphQL
+/// OCaml
+/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
+/// WebGL
+/// TensorFlow
+/// TrueType
+/// iOS macOS FreeBSD
+/// TeX LaTeX BibTeX BibLaTeX
+/// MinGW
+/// CamelCase (see also #2395)
+/// `be_sure_we_got_to_the_end_of_it`
+fn test_allowed() {
+}
+
+/// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
+/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
+/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
+/// It can also be [`inline_link2`].
+///
+/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
+/// [inline_link]: https://foobar
+/// [inline_link2]: https://foobar
+/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
+/// `multiline_ticks` functions.
+///
+/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
+/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
+/// `be_sure_we_got_to_the_end_of_it`
+fn main() {
+ foo_bar();
+ multiline_codeblock();
+ test_emphasis();
+ test_units();
+}
+
+/// ## `CamelCaseThing`
+/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
+///
+/// # `CamelCaseThing`
+///
+/// Not a title #897 `CamelCaseThing`
+/// `be_sure_we_got_to_the_end_of_it`
+fn issue897() {
+}
+
+/// I am confused by brackets? (`x_y`)
+/// I am confused by brackets? (foo `x_y`)
+/// I am confused by brackets? (`x_y` foo)
+/// `be_sure_we_got_to_the_end_of_it`
+fn issue900() {
+}
+
+/// Diesel queries also have a similar problem to [Iterator][iterator], where
+/// /// More talking
+/// returning them from a function requires exposing the implementation of that
+/// function. The [`helper_types`][helper_types] module exists to help with this,
+/// but you might want to hide the return type or have it conditionally change.
+/// Boxing can achieve both.
+///
+/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
+/// [helper_types]: ../helper_types/index.html
+/// `be_sure_we_got_to_the_end_of_it`
+fn issue883() {
+}
+
+/// `foo_bar
+/// baz_quz`
+/// [foo
+/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
+fn multiline() {
+}
+
+/** E.g., serialization of an empty list: `FooBar`
+```
+That's in a code block: `PackedNode`
+```
+
+And `BarQuz` too.
+`be_sure_we_got_to_the_end_of_it`
+*/
+fn issue1073() {
+}
+
+/** E.g., serialization of an empty list: `FooBar`
+```
+That's in a code block: PackedNode
+```
+
+And `BarQuz` too.
+`be_sure_we_got_to_the_end_of_it`
+*/
+fn issue1073_alt() {
+}
+
+/// Tests more than three quotes:
+/// ````
+/// DoNotWarn
+/// ```
+/// StillDont
+/// ````
+/// `be_sure_we_got_to_the_end_of_it`
+fn four_quotes() {
+}
+
+#[cfg_attr(feature = "a", doc = " ```")]
+#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
+/// fn main() {
+/// let s = "localhost:10000".to_string();
+/// println!("{}", s);
+/// }
+/// ```
+fn issue_1469() {}
+
+/**
+ * This is a doc comment that should not be a list
+ *This would also be an error under a strict common mark interpretation
+ */
+fn issue_1920() {}
+
+/// An iterator over `mycrate::Collection`'s values.
+/// It should not lint a `'static` lifetime in ticks.
+fn issue_2210() {}
+
+/// This should not cause the lint to trigger:
+/// #REQ-data-family.lint_partof_exists
+fn issue_2343() {}
+
+/// This should not cause an ICE:
+/// __|_ _|__||_|
+fn pulldown_cmark_crash() {}
+
+/// This should not lint
+/// (regression test for #7758)
+/// [plain text][path::to::item]
+fn intra_doc_link() {}
+
+// issue #7033 - generic_const_exprs ICE
+struct S<T, const N: usize>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ arr: [T; N.checked_next_power_of_two().unwrap()],
+ n: usize,
+}
+
+impl<T: Copy + Default, const N: usize> S<T, N>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ fn new() -> Self {
+ Self {
+ arr: [T::default(); N.checked_next_power_of_two().unwrap()],
+ n: 0,
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
new file mode 100644
index 000000000..f3cf96615
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -0,0 +1,215 @@
+// run-rustfix
+//! This file tests for the `DOC_MARKDOWN` lint.
+
+#![allow(dead_code, incomplete_features)]
+#![warn(clippy::doc_markdown)]
+#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
+#![rustfmt::skip]
+
+/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
+/// which should be reported only once despite being __doubly bad__.
+/// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
+/// Import an item from ::awesome::global::blob:: (Intended postfix)
+/// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
+/// That's not code ~NotInCodeBlock~.
+/// be_sure_we_got_to_the_end_of_it
+fn foo_bar() {
+}
+
+/// That one tests multiline ticks.
+/// ```rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ```
+///
+/// ~~~rust
+/// foo_bar FOO_BAR
+/// _foo bar_
+/// ~~~
+/// be_sure_we_got_to_the_end_of_it
+fn multiline_codeblock() {
+}
+
+/// This _is a test for
+/// multiline
+/// emphasis_.
+/// be_sure_we_got_to_the_end_of_it
+fn test_emphasis() {
+}
+
+/// This tests units. See also #835.
+/// kiB MiB GiB TiB PiB EiB
+/// kib Mib Gib Tib Pib Eib
+/// kB MB GB TB PB EB
+/// kb Mb Gb Tb Pb Eb
+/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
+/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
+/// 32kB 32MB 32GB 32TB 32PB 32EB
+/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
+/// NaN
+/// be_sure_we_got_to_the_end_of_it
+fn test_units() {
+}
+
+/// This tests allowed identifiers.
+/// KiB MiB GiB TiB PiB EiB
+/// DirectX
+/// ECMAScript
+/// GPLv2 GPLv3
+/// GitHub GitLab
+/// IPv4 IPv6
+/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// NaN NaNs
+/// OAuth GraphQL
+/// OCaml
+/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
+/// WebGL
+/// TensorFlow
+/// TrueType
+/// iOS macOS FreeBSD
+/// TeX LaTeX BibTeX BibLaTeX
+/// MinGW
+/// CamelCase (see also #2395)
+/// be_sure_we_got_to_the_end_of_it
+fn test_allowed() {
+}
+
+/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
+/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
+/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
+/// It can also be [inline_link2].
+///
+/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
+/// [inline_link]: https://foobar
+/// [inline_link2]: https://foobar
+/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
+/// `multiline_ticks` functions.
+///
+/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
+/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
+/// be_sure_we_got_to_the_end_of_it
+fn main() {
+ foo_bar();
+ multiline_codeblock();
+ test_emphasis();
+ test_units();
+}
+
+/// ## CamelCaseThing
+/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
+///
+/// # CamelCaseThing
+///
+/// Not a title #897 CamelCaseThing
+/// be_sure_we_got_to_the_end_of_it
+fn issue897() {
+}
+
+/// I am confused by brackets? (`x_y`)
+/// I am confused by brackets? (foo `x_y`)
+/// I am confused by brackets? (`x_y` foo)
+/// be_sure_we_got_to_the_end_of_it
+fn issue900() {
+}
+
+/// Diesel queries also have a similar problem to [Iterator][iterator], where
+/// /// More talking
+/// returning them from a function requires exposing the implementation of that
+/// function. The [`helper_types`][helper_types] module exists to help with this,
+/// but you might want to hide the return type or have it conditionally change.
+/// Boxing can achieve both.
+///
+/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
+/// [helper_types]: ../helper_types/index.html
+/// be_sure_we_got_to_the_end_of_it
+fn issue883() {
+}
+
+/// `foo_bar
+/// baz_quz`
+/// [foo
+/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
+fn multiline() {
+}
+
+/** E.g., serialization of an empty list: FooBar
+```
+That's in a code block: `PackedNode`
+```
+
+And BarQuz too.
+be_sure_we_got_to_the_end_of_it
+*/
+fn issue1073() {
+}
+
+/** E.g., serialization of an empty list: FooBar
+```
+That's in a code block: PackedNode
+```
+
+And BarQuz too.
+be_sure_we_got_to_the_end_of_it
+*/
+fn issue1073_alt() {
+}
+
+/// Tests more than three quotes:
+/// ````
+/// DoNotWarn
+/// ```
+/// StillDont
+/// ````
+/// be_sure_we_got_to_the_end_of_it
+fn four_quotes() {
+}
+
+#[cfg_attr(feature = "a", doc = " ```")]
+#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
+/// fn main() {
+/// let s = "localhost:10000".to_string();
+/// println!("{}", s);
+/// }
+/// ```
+fn issue_1469() {}
+
+/**
+ * This is a doc comment that should not be a list
+ *This would also be an error under a strict common mark interpretation
+ */
+fn issue_1920() {}
+
+/// An iterator over mycrate::Collection's values.
+/// It should not lint a `'static` lifetime in ticks.
+fn issue_2210() {}
+
+/// This should not cause the lint to trigger:
+/// #REQ-data-family.lint_partof_exists
+fn issue_2343() {}
+
+/// This should not cause an ICE:
+/// __|_ _|__||_|
+fn pulldown_cmark_crash() {}
+
+/// This should not lint
+/// (regression test for #7758)
+/// [plain text][path::to::item]
+fn intra_doc_link() {}
+
+// issue #7033 - generic_const_exprs ICE
+struct S<T, const N: usize>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ arr: [T; N.checked_next_power_of_two().unwrap()],
+ n: usize,
+}
+
+impl<T: Copy + Default, const N: usize> S<T, N>
+where [(); N.checked_next_power_of_two().unwrap()]: {
+ fn new() -> Self {
+ Self {
+ arr: [T::default(); N.checked_next_power_of_two().unwrap()],
+ n: 0,
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
new file mode 100644
index 000000000..40345370c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -0,0 +1,333 @@
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:9:9
+ |
+LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+ | ^^^^^^^
+ |
+ = note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+ |
+LL | /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
+ | ~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:9:51
+ |
+LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
+ | ^^^^^^^^
+ |
+help: try
+ |
+LL | /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
+ | ~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:10:83
+ |
+LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
+ | ^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not `Foo::some_fun`
+ | ~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:12:13
+ |
+LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
+ | ^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Here be `::a::global:path`, and _::another::global::path_. :: is not a path though.
+ | ~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:12:36
+ |
+LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Here be ::a::global:path, and _`::another::global::path`_. :: is not a path though.
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:13:25
+ |
+LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Import an item from `::awesome::global::blob::` (Intended postfix)
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:14:31
+ |
+LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
+ | ^^^^^
+ |
+help: try
+ |
+LL | /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+ | ~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:15:22
+ |
+LL | /// That's not code ~NotInCodeBlock~.
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// That's not code ~`NotInCodeBlock`~.
+ | ~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:16:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:30:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:37:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:51:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:74:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:78:22
+ |
+LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:81:21
+ |
+LL | /// It can also be [inline_link2].
+ | ^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// It can also be [`inline_link2`].
+ | ~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:91:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:99:8
+ |
+LL | /// ## CamelCaseThing
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// ## `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:102:7
+ |
+LL | /// # CamelCaseThing
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// # `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:104:22
+ |
+LL | /// Not a title #897 CamelCaseThing
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Not a title #897 `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:105:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:112:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:125:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:136:43
+ |
+LL | /** E.g., serialization of an empty list: FooBar
+ | ^^^^^^
+ |
+help: try
+ |
+LL | /** E.g., serialization of an empty list: `FooBar`
+ | ~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:141:5
+ |
+LL | And BarQuz too.
+ | ^^^^^^
+ |
+help: try
+ |
+LL | And `BarQuz` too.
+ | ~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:142:1
+ |
+LL | be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | `be_sure_we_got_to_the_end_of_it`
+ |
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:147:43
+ |
+LL | /** E.g., serialization of an empty list: FooBar
+ | ^^^^^^
+ |
+help: try
+ |
+LL | /** E.g., serialization of an empty list: `FooBar`
+ | ~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:152:5
+ |
+LL | And BarQuz too.
+ | ^^^^^^
+ |
+help: try
+ |
+LL | And `BarQuz` too.
+ | ~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:153:1
+ |
+LL | be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | `be_sure_we_got_to_the_end_of_it`
+ |
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:164:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc-fixable.rs:183:22
+ |
+LL | /// An iterator over mycrate::Collection's values.
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// An iterator over `mycrate::Collection`'s values.
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 30 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc/issue_1832.rs b/src/tools/clippy/tests/ui/doc/issue_1832.rs
new file mode 100644
index 000000000..10586f16d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/issue_1832.rs
@@ -0,0 +1,9 @@
+/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
+///
+/// Not ok: http://www.unicode.org
+/// Not ok: https://www.unicode.org
+/// Not ok: http://www.unicode.org/
+/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
+fn issue_1832() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/doc/issue_902.rs b/src/tools/clippy/tests/ui/doc/issue_902.rs
new file mode 100644
index 000000000..4b0c835dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/issue_902.rs
@@ -0,0 +1,7 @@
+/// See [NIST SP 800-56A, revision 2].
+///
+/// [NIST SP 800-56A, revision 2]:
+/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419
+fn issue_902_comment() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
new file mode 100644
index 000000000..8e8324b30
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
@@ -0,0 +1,43 @@
+//! This file tests for the `DOC_MARKDOWN` lint, specifically cases
+//! where ticks are unbalanced (see issue #6753).
+
+#![allow(dead_code)]
+#![warn(clippy::doc_markdown)]
+
+/// This is a doc comment with `unbalanced_tick marks and several words that
+/// should be `encompassed_by` tick marks because they `contain_underscores`.
+/// Because of the initial `unbalanced_tick` pair, the error message is
+/// very `confusing_and_misleading`.
+fn main() {}
+
+/// This paragraph has `unbalanced_tick marks and should stop_linting.
+///
+/// This paragraph is fine and should_be linted normally.
+///
+/// Double unbalanced backtick from ``here to here` should lint.
+///
+/// Double balanced back ticks ``start end`` is fine.
+fn multiple_paragraphs() {}
+
+/// ```
+/// // Unbalanced tick mark in code block shouldn't warn:
+/// `
+/// ```
+fn in_code_block() {}
+
+/// # `Fine`
+///
+/// ## not_fine
+///
+/// ### `unbalanced
+///
+/// - This `item has unbalanced tick marks
+/// - This item needs backticks_here
+fn other_markdown() {}
+
+#[rustfmt::skip]
+/// - ```rust
+/// /// `lol`
+/// pub struct Struct;
+/// ```
+fn iss_7421() {}
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
new file mode 100644
index 000000000..a462b9887
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
@@ -0,0 +1,79 @@
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:7:1
+ |
+LL | / /// This is a doc comment with `unbalanced_tick marks and several words that
+LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`.
+LL | | /// Because of the initial `unbalanced_tick` pair, the error message is
+LL | | /// very `confusing_and_misleading`.
+ | |____________________________________^
+ |
+ = note: `-D clippy::doc-markdown` implied by `-D warnings`
+ = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:13:1
+ |
+LL | /// This paragraph has `unbalanced_tick marks and should stop_linting.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
+error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:15:32
+ |
+LL | /// This paragraph is fine and should_be linted normally.
+ | ^^^^^^^^^
+ |
+help: try
+ |
+LL | /// This paragraph is fine and `should_be` linted normally.
+ | ~~~~~~~~~~~
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:17:1
+ |
+LL | /// Double unbalanced backtick from ``here to here` should lint.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
+error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:30:8
+ |
+LL | /// ## not_fine
+ | ^^^^^^^^
+ |
+help: try
+ |
+LL | /// ## `not_fine`
+ | ~~~~~~~~~~
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:32:1
+ |
+LL | /// ### `unbalanced
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+ --> $DIR/unbalanced_ticks.rs:34:1
+ |
+LL | /// - This `item has unbalanced tick marks
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: a backtick may be missing a pair
+
+error: item in documentation is missing backticks
+ --> $DIR/unbalanced_ticks.rs:35:23
+ |
+LL | /// - This item needs backticks_here
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// - This item needs `backticks_here`
+ | ~~~~~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc_errors.rs b/src/tools/clippy/tests/ui/doc_errors.rs
new file mode 100644
index 000000000..30fdd3b08
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_errors.rs
@@ -0,0 +1,104 @@
+#![warn(clippy::missing_errors_doc)]
+#![allow(clippy::result_unit_err)]
+#![allow(clippy::unnecessary_wraps)]
+
+use std::io;
+
+pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This is not sufficiently documented.
+pub fn pub_fn_returning_io_result() -> io::Result<()> {
+ unimplemented!();
+}
+
+/// This is not sufficiently documented.
+pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
+ unimplemented!();
+}
+
+/// # Errors
+/// A description of the errors goes here.
+pub fn pub_fn_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// # Errors
+/// A description of the errors goes here.
+pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This function doesn't require the documentation because it is private
+fn priv_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+/// This function doesn't require the documentation because it is private
+async fn async_priv_fn_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+}
+
+pub struct Struct1;
+
+impl Struct1 {
+ /// This is not sufficiently documented.
+ pub fn pub_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This is not sufficiently documented.
+ pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// # Errors
+ /// A description of the errors goes here.
+ pub fn pub_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// # Errors
+ /// A description of the errors goes here.
+ pub async fn async_pub_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This function doesn't require the documentation because it is private.
+ fn priv_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ /// This function doesn't require the documentation because it is private.
+ async fn async_priv_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+}
+
+pub trait Trait1 {
+ /// This is not sufficiently documented.
+ fn trait_method_missing_errors_header() -> Result<(), ()>;
+
+ /// # Errors
+ /// A description of the errors goes here.
+ fn trait_method_with_errors_header() -> Result<(), ()>;
+}
+
+impl Trait1 for Struct1 {
+ fn trait_method_missing_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+
+ fn trait_method_with_errors_header() -> Result<(), ()> {
+ unimplemented!();
+ }
+}
+
+fn main() -> Result<(), ()> {
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/doc_errors.stderr b/src/tools/clippy/tests/ui/doc_errors.stderr
new file mode 100644
index 000000000..c7b616e28
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_errors.stderr
@@ -0,0 +1,58 @@
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:7:1
+ |
+LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-errors-doc` implied by `-D warnings`
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:11:1
+ |
+LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:16:1
+ |
+LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:21:1
+ |
+LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:51:5
+ |
+LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:56:5
+ |
+LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: docs for function returning `Result` missing `# Errors` section
+ --> $DIR/doc_errors.rs:85:5
+ |
+LL | fn trait_method_missing_errors_header() -> Result<(), ()>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc_link_with_quotes.rs b/src/tools/clippy/tests/ui/doc_link_with_quotes.rs
new file mode 100644
index 000000000..ab52fb1a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_link_with_quotes.rs
@@ -0,0 +1,12 @@
+#![warn(clippy::doc_link_with_quotes)]
+
+fn main() {
+ foo()
+}
+
+/// Calls ['bar']
+pub fn foo() {
+ bar()
+}
+
+pub fn bar() {}
diff --git a/src/tools/clippy/tests/ui/doc_link_with_quotes.stderr b/src/tools/clippy/tests/ui/doc_link_with_quotes.stderr
new file mode 100644
index 000000000..bf6d57d8a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_link_with_quotes.stderr
@@ -0,0 +1,10 @@
+error: possible intra-doc link using quotes instead of backticks
+ --> $DIR/doc_link_with_quotes.rs:7:1
+ |
+LL | /// Calls ['bar']
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs
new file mode 100644
index 000000000..b91f7aa0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_unsafe.rs
@@ -0,0 +1,134 @@
+// aux-build:doc_unsafe_macros.rs
+
+#![allow(clippy::let_unit_value)]
+
+#[macro_use]
+extern crate doc_unsafe_macros;
+
+/// This is not sufficiently documented
+pub unsafe fn destroy_the_planet() {
+ unimplemented!();
+}
+
+/// This one is
+///
+/// # Safety
+///
+/// This function shouldn't be called unless the horsemen are ready
+pub unsafe fn apocalypse(universe: &mut ()) {
+ unimplemented!();
+}
+
+/// This is a private function, so docs aren't necessary
+unsafe fn you_dont_see_me() {
+ unimplemented!();
+}
+
+mod private_mod {
+ pub unsafe fn only_crate_wide_accessible() {
+ unimplemented!();
+ }
+
+ pub unsafe fn republished() {
+ unimplemented!();
+ }
+}
+
+pub use private_mod::republished;
+
+pub trait SafeTraitUnsafeMethods {
+ unsafe fn woefully_underdocumented(self);
+
+ /// # Safety
+ unsafe fn at_least_somewhat_documented(self);
+}
+
+pub unsafe trait UnsafeTrait {
+ fn method();
+}
+
+/// # Safety
+pub unsafe trait DocumentedUnsafeTrait {
+ fn method2();
+}
+
+pub struct Struct;
+
+impl SafeTraitUnsafeMethods for Struct {
+ unsafe fn woefully_underdocumented(self) {
+ // all is well
+ }
+
+ unsafe fn at_least_somewhat_documented(self) {
+ // all is still well
+ }
+}
+
+unsafe impl UnsafeTrait for Struct {
+ fn method() {}
+}
+
+unsafe impl DocumentedUnsafeTrait for Struct {
+ fn method2() {}
+}
+
+impl Struct {
+ pub unsafe fn more_undocumented_unsafe() -> Self {
+ unimplemented!();
+ }
+
+ /// # Safety
+ pub unsafe fn somewhat_documented(&self) {
+ unimplemented!();
+ }
+
+ unsafe fn private(&self) {
+ unimplemented!();
+ }
+}
+
+macro_rules! very_unsafe {
+ () => {
+ pub unsafe fn whee() {
+ unimplemented!()
+ }
+
+ /// # Safety
+ ///
+ /// Please keep the seat belt fastened
+ pub unsafe fn drive() {
+ whee()
+ }
+ };
+}
+
+very_unsafe!();
+
+// we don't lint code from external macros
+undocd_unsafe!();
+
+fn main() {
+ unsafe {
+ you_dont_see_me();
+ destroy_the_planet();
+ let mut universe = ();
+ apocalypse(&mut universe);
+ private_mod::only_crate_wide_accessible();
+ drive();
+ }
+}
+
+// do not lint if any parent has `#[doc(hidden)]` attribute
+// see #7347
+#[doc(hidden)]
+pub mod __macro {
+ pub struct T;
+ impl T {
+ pub unsafe fn f() {}
+ }
+}
+
+/// # Implementation safety
+pub unsafe trait DocumentedUnsafeTraitWithImplementationHeader {
+ fn method();
+}
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr
new file mode 100644
index 000000000..904b88eae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr
@@ -0,0 +1,55 @@
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:9:1
+ |
+LL | / pub unsafe fn destroy_the_planet() {
+LL | | unimplemented!();
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:32:5
+ |
+LL | / pub unsafe fn republished() {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:40:5
+ |
+LL | unsafe fn woefully_underdocumented(self);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for unsafe trait missing `# Safety` section
+ --> $DIR/doc_unsafe.rs:46:1
+ |
+LL | / pub unsafe trait UnsafeTrait {
+LL | | fn method();
+LL | | }
+ | |_^
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:76:5
+ |
+LL | / pub unsafe fn more_undocumented_unsafe() -> Self {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: unsafe function's docs miss `# Safety` section
+ --> $DIR/doc_unsafe.rs:92:9
+ |
+LL | / pub unsafe fn whee() {
+LL | | unimplemented!()
+LL | | }
+ | |_________^
+...
+LL | very_unsafe!();
+ | -------------- in this macro invocation
+ |
+ = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/double_comparison.fixed b/src/tools/clippy/tests/ui/double_comparison.fixed
new file mode 100644
index 000000000..bb6cdaa66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_comparison.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+
+fn main() {
+ let x = 1;
+ let y = 2;
+ if x <= y {
+ // do something
+ }
+ if x <= y {
+ // do something
+ }
+ if x >= y {
+ // do something
+ }
+ if x >= y {
+ // do something
+ }
+ if x != y {
+ // do something
+ }
+ if x != y {
+ // do something
+ }
+ if x == y {
+ // do something
+ }
+ if x == y {
+ // do something
+ }
+}
diff --git a/src/tools/clippy/tests/ui/double_comparison.rs b/src/tools/clippy/tests/ui/double_comparison.rs
new file mode 100644
index 000000000..9a2a9068a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_comparison.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+
+fn main() {
+ let x = 1;
+ let y = 2;
+ if x == y || x < y {
+ // do something
+ }
+ if x < y || x == y {
+ // do something
+ }
+ if x == y || x > y {
+ // do something
+ }
+ if x > y || x == y {
+ // do something
+ }
+ if x < y || x > y {
+ // do something
+ }
+ if x > y || x < y {
+ // do something
+ }
+ if x <= y && x >= y {
+ // do something
+ }
+ if x >= y && x <= y {
+ // do something
+ }
+}
diff --git a/src/tools/clippy/tests/ui/double_comparison.stderr b/src/tools/clippy/tests/ui/double_comparison.stderr
new file mode 100644
index 000000000..05ef4e25f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_comparison.stderr
@@ -0,0 +1,52 @@
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:6:8
+ |
+LL | if x == y || x < y {
+ | ^^^^^^^^^^^^^^^ help: try: `x <= y`
+ |
+ = note: `-D clippy::double-comparisons` implied by `-D warnings`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:9:8
+ |
+LL | if x < y || x == y {
+ | ^^^^^^^^^^^^^^^ help: try: `x <= y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:12:8
+ |
+LL | if x == y || x > y {
+ | ^^^^^^^^^^^^^^^ help: try: `x >= y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:15:8
+ |
+LL | if x > y || x == y {
+ | ^^^^^^^^^^^^^^^ help: try: `x >= y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:18:8
+ |
+LL | if x < y || x > y {
+ | ^^^^^^^^^^^^^^ help: try: `x != y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:21:8
+ |
+LL | if x > y || x < y {
+ | ^^^^^^^^^^^^^^ help: try: `x != y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:24:8
+ |
+LL | if x <= y && x >= y {
+ | ^^^^^^^^^^^^^^^^ help: try: `x == y`
+
+error: this binary expression can be simplified
+ --> $DIR/double_comparison.rs:27:8
+ |
+LL | if x >= y && x <= y {
+ | ^^^^^^^^^^^^^^^^ help: try: `x == y`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs
new file mode 100644
index 000000000..05e087b08
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_must_use.rs
@@ -0,0 +1,28 @@
+#![warn(clippy::double_must_use)]
+#![allow(clippy::result_unit_err)]
+
+#[must_use]
+pub fn must_use_result() -> Result<(), ()> {
+ unimplemented!();
+}
+
+#[must_use]
+pub fn must_use_tuple() -> (Result<(), ()>, u8) {
+ unimplemented!();
+}
+
+#[must_use]
+pub fn must_use_array() -> [Result<(), ()>; 1] {
+ unimplemented!();
+}
+
+#[must_use = "With note"]
+pub fn must_use_with_note() -> Result<(), ()> {
+ unimplemented!();
+}
+
+fn main() {
+ must_use_result();
+ must_use_tuple();
+ must_use_with_note();
+}
diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr
new file mode 100644
index 000000000..8290ece1c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_must_use.stderr
@@ -0,0 +1,27 @@
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:5:1
+ |
+LL | pub fn must_use_result() -> Result<(), ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::double-must-use` implied by `-D warnings`
+ = help: either add some descriptive text or remove the attribute
+
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:10:1
+ |
+LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: either add some descriptive text or remove the attribute
+
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:15:1
+ |
+LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: either add some descriptive text or remove the attribute
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/double_neg.rs b/src/tools/clippy/tests/ui/double_neg.rs
new file mode 100644
index 000000000..38a8fbd74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_neg.rs
@@ -0,0 +1,8 @@
+#[warn(clippy::double_neg)]
+#[allow(clippy::no_effect)]
+fn main() {
+ let x = 1;
+ -x;
+ -(-x);
+ --x;
+}
diff --git a/src/tools/clippy/tests/ui/double_neg.stderr b/src/tools/clippy/tests/ui/double_neg.stderr
new file mode 100644
index 000000000..7cdb040b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_neg.stderr
@@ -0,0 +1,10 @@
+error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op
+ --> $DIR/double_neg.rs:7:5
+ |
+LL | --x;
+ | ^^^
+ |
+ = note: `-D clippy::double-neg` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/double_parens.rs b/src/tools/clippy/tests/ui/double_parens.rs
new file mode 100644
index 000000000..ff1dc76ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_parens.rs
@@ -0,0 +1,56 @@
+#![warn(clippy::double_parens)]
+#![allow(dead_code, clippy::eq_op)]
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+fn dummy_fn<T>(_: T) {}
+
+struct DummyStruct;
+
+impl DummyStruct {
+ fn dummy_method<T>(self, _: T) {}
+}
+
+fn simple_double_parens() -> i32 {
+ ((0))
+}
+
+fn fn_double_parens() {
+ dummy_fn((0));
+}
+
+fn method_double_parens(x: DummyStruct) {
+ x.dummy_method((0));
+}
+
+fn tuple_double_parens() -> (i32, i32) {
+ ((1, 2))
+}
+
+fn unit_double_parens() {
+ (())
+}
+
+fn fn_tuple_ok() {
+ dummy_fn((1, 2));
+}
+
+fn method_tuple_ok(x: DummyStruct) {
+ x.dummy_method((1, 2));
+}
+
+fn fn_unit_ok() {
+ dummy_fn(());
+}
+
+fn method_unit_ok(x: DummyStruct) {
+ x.dummy_method(());
+}
+
+// Issue #3206
+fn inside_macro() {
+ assert_eq!((1, 2), (1, 2), "Error");
+ assert_eq!(((1, 2)), (1, 2), "Error");
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/double_parens.stderr b/src/tools/clippy/tests/ui/double_parens.stderr
new file mode 100644
index 000000000..40fcad2ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/double_parens.stderr
@@ -0,0 +1,40 @@
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:15:5
+ |
+LL | ((0))
+ | ^^^^^
+ |
+ = note: `-D clippy::double-parens` implied by `-D warnings`
+
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:19:14
+ |
+LL | dummy_fn((0));
+ | ^^^
+
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:23:20
+ |
+LL | x.dummy_method((0));
+ | ^^^
+
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:27:5
+ |
+LL | ((1, 2))
+ | ^^^^^^^^
+
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:31:5
+ |
+LL | (())
+ | ^^^^
+
+error: consider removing unnecessary double parentheses
+ --> $DIR/double_parens.rs:53:16
+ |
+LL | assert_eq!(((1, 2)), (1, 2), "Error");
+ | ^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.rs b/src/tools/clippy/tests/ui/drop_forget_copy.rs
new file mode 100644
index 000000000..7c7a9ecff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_forget_copy.rs
@@ -0,0 +1,66 @@
+#![warn(clippy::drop_copy, clippy::forget_copy)]
+#![allow(clippy::toplevel_ref_arg, clippy::drop_ref, clippy::forget_ref, unused_mut)]
+
+use std::mem::{drop, forget};
+use std::vec::Vec;
+
+#[derive(Copy, Clone)]
+struct SomeStruct;
+
+struct AnotherStruct {
+ x: u8,
+ y: u8,
+ z: Vec<u8>,
+}
+
+impl Clone for AnotherStruct {
+ fn clone(&self) -> AnotherStruct {
+ AnotherStruct {
+ x: self.x,
+ y: self.y,
+ z: self.z.clone(),
+ }
+ }
+}
+
+fn main() {
+ let s1 = SomeStruct {};
+ let s2 = s1;
+ let s3 = &s1;
+ let mut s4 = s1;
+ let ref s5 = s1;
+
+ drop(s1);
+ drop(s2);
+ drop(s3);
+ drop(s4);
+ drop(s5);
+
+ forget(s1);
+ forget(s2);
+ forget(s3);
+ forget(s4);
+ forget(s5);
+
+ let a1 = AnotherStruct {
+ x: 255,
+ y: 0,
+ z: vec![1, 2, 3],
+ };
+ let a2 = &a1;
+ let mut a3 = a1.clone();
+ let ref a4 = a1;
+ let a5 = a1.clone();
+
+ drop(a2);
+ drop(a3);
+ drop(a4);
+ drop(a5);
+
+ forget(a2);
+ let a3 = &a1;
+ forget(a3);
+ forget(a4);
+ let a5 = a1.clone();
+ forget(a5);
+}
diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.stderr b/src/tools/clippy/tests/ui/drop_forget_copy.stderr
new file mode 100644
index 000000000..88228afae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_forget_copy.stderr
@@ -0,0 +1,76 @@
+error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:33:5
+ |
+LL | drop(s1);
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::drop-copy` implied by `-D warnings`
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:33:10
+ |
+LL | drop(s1);
+ | ^^
+
+error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:34:5
+ |
+LL | drop(s2);
+ | ^^^^^^^^
+ |
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:34:10
+ |
+LL | drop(s2);
+ | ^^
+
+error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:36:5
+ |
+LL | drop(s4);
+ | ^^^^^^^^
+ |
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:36:10
+ |
+LL | drop(s4);
+ | ^^
+
+error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:39:5
+ |
+LL | forget(s1);
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::forget-copy` implied by `-D warnings`
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:39:12
+ |
+LL | forget(s1);
+ | ^^
+
+error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:40:5
+ |
+LL | forget(s2);
+ | ^^^^^^^^^^
+ |
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:40:12
+ |
+LL | forget(s2);
+ | ^^
+
+error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact
+ --> $DIR/drop_forget_copy.rs:42:5
+ |
+LL | forget(s4);
+ | ^^^^^^^^^^
+ |
+note: argument has type `SomeStruct`
+ --> $DIR/drop_forget_copy.rs:42:12
+ |
+LL | forget(s4);
+ | ^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/drop_non_drop.rs b/src/tools/clippy/tests/ui/drop_non_drop.rs
new file mode 100644
index 000000000..5a0ebde82
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_non_drop.rs
@@ -0,0 +1,40 @@
+#![warn(clippy::drop_non_drop)]
+
+use core::mem::drop;
+
+fn make_result<T>(t: T) -> Result<T, ()> {
+ Ok(t)
+}
+
+#[must_use]
+fn must_use<T>(t: T) -> T {
+ t
+}
+
+fn drop_generic<T>(t: T) {
+ // Don't lint
+ drop(t)
+}
+
+fn main() {
+ struct Foo;
+ // Lint
+ drop(Foo);
+ // Don't lint
+ drop(make_result(Foo));
+ // Don't lint
+ drop(must_use(Foo));
+
+ struct Bar;
+ impl Drop for Bar {
+ fn drop(&mut self) {}
+ }
+ // Don't lint
+ drop(Bar);
+
+ struct Baz<T>(T);
+ // Lint
+ drop(Baz(Foo));
+ // Don't lint
+ drop(Baz(Bar));
+}
diff --git a/src/tools/clippy/tests/ui/drop_non_drop.stderr b/src/tools/clippy/tests/ui/drop_non_drop.stderr
new file mode 100644
index 000000000..30121033d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_non_drop.stderr
@@ -0,0 +1,27 @@
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
+ --> $DIR/drop_non_drop.rs:22:5
+ |
+LL | drop(Foo);
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::drop-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+ --> $DIR/drop_non_drop.rs:22:10
+ |
+LL | drop(Foo);
+ | ^^^
+
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
+ --> $DIR/drop_non_drop.rs:37:5
+ |
+LL | drop(Baz(Foo));
+ | ^^^^^^^^^^^^^^
+ |
+note: argument has type `main::Baz<main::Foo>`
+ --> $DIR/drop_non_drop.rs:37:10
+ |
+LL | drop(Baz(Foo));
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/drop_ref.rs b/src/tools/clippy/tests/ui/drop_ref.rs
new file mode 100644
index 000000000..7de0b0bbd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_ref.rs
@@ -0,0 +1,74 @@
+#![warn(clippy::drop_ref)]
+#![allow(clippy::toplevel_ref_arg)]
+#![allow(clippy::map_err_ignore)]
+#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)]
+
+use std::mem::drop;
+
+struct SomeStruct;
+
+fn main() {
+ drop(&SomeStruct);
+
+ let mut owned1 = SomeStruct;
+ drop(&owned1);
+ drop(&&owned1);
+ drop(&mut owned1);
+ drop(owned1); //OK
+
+ let reference1 = &SomeStruct;
+ drop(reference1);
+
+ let reference2 = &mut SomeStruct;
+ drop(reference2);
+
+ let ref reference3 = SomeStruct;
+ drop(reference3);
+}
+
+#[allow(dead_code)]
+fn test_generic_fn_drop<T>(val: T) {
+ drop(&val);
+ drop(val); //OK
+}
+
+#[allow(dead_code)]
+fn test_similarly_named_function() {
+ fn drop<T>(_val: T) {}
+ drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name
+ std::mem::drop(&SomeStruct);
+}
+
+#[derive(Copy, Clone)]
+pub struct Error;
+fn produce_half_owl_error() -> Result<(), Error> {
+ Ok(())
+}
+
+fn produce_half_owl_ok() -> Result<bool, ()> {
+ Ok(true)
+}
+
+#[allow(dead_code)]
+fn test_owl_result() -> Result<(), ()> {
+ produce_half_owl_error().map_err(|_| ())?;
+ produce_half_owl_ok().map(|_| ())?;
+ // the following should not be linted,
+ // we should not force users to use toilet closures
+ // to produce owl results when drop is more convenient
+ produce_half_owl_error().map_err(drop)?;
+ produce_half_owl_ok().map_err(drop)?;
+ Ok(())
+}
+
+#[allow(dead_code)]
+fn test_owl_result_2() -> Result<u8, ()> {
+ produce_half_owl_error().map_err(|_| ())?;
+ produce_half_owl_ok().map(|_| ())?;
+ // the following should not be linted,
+ // we should not force users to use toilet closures
+ // to produce owl results when drop is more convenient
+ produce_half_owl_error().map_err(drop)?;
+ produce_half_owl_ok().map(drop)?;
+ Ok(1)
+}
diff --git a/src/tools/clippy/tests/ui/drop_ref.stderr b/src/tools/clippy/tests/ui/drop_ref.stderr
new file mode 100644
index 000000000..531849f06
--- /dev/null
+++ b/src/tools/clippy/tests/ui/drop_ref.stderr
@@ -0,0 +1,111 @@
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:11:5
+ |
+LL | drop(&SomeStruct);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::drop-ref` implied by `-D warnings`
+note: argument has type `&SomeStruct`
+ --> $DIR/drop_ref.rs:11:10
+ |
+LL | drop(&SomeStruct);
+ | ^^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:14:5
+ |
+LL | drop(&owned1);
+ | ^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/drop_ref.rs:14:10
+ |
+LL | drop(&owned1);
+ | ^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:15:5
+ |
+LL | drop(&&owned1);
+ | ^^^^^^^^^^^^^^
+ |
+note: argument has type `&&SomeStruct`
+ --> $DIR/drop_ref.rs:15:10
+ |
+LL | drop(&&owned1);
+ | ^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:16:5
+ |
+LL | drop(&mut owned1);
+ | ^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&mut SomeStruct`
+ --> $DIR/drop_ref.rs:16:10
+ |
+LL | drop(&mut owned1);
+ | ^^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:20:5
+ |
+LL | drop(reference1);
+ | ^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/drop_ref.rs:20:10
+ |
+LL | drop(reference1);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:23:5
+ |
+LL | drop(reference2);
+ | ^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&mut SomeStruct`
+ --> $DIR/drop_ref.rs:23:10
+ |
+LL | drop(reference2);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:26:5
+ |
+LL | drop(reference3);
+ | ^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/drop_ref.rs:26:10
+ |
+LL | drop(reference3);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:31:5
+ |
+LL | drop(&val);
+ | ^^^^^^^^^^
+ |
+note: argument has type `&T`
+ --> $DIR/drop_ref.rs:31:10
+ |
+LL | drop(&val);
+ | ^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:39:5
+ |
+LL | std::mem::drop(&SomeStruct);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/drop_ref.rs:39:20
+ |
+LL | std::mem::drop(&SomeStruct);
+ | ^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
new file mode 100644
index 000000000..54d748c7c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::duplicate_underscore_argument)]
+#[allow(dead_code, unused)]
+
+fn join_the_dark_side(darth: i32, _darth: i32) {}
+fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one
+
+fn main() {
+ join_the_dark_side(0, 0);
+ join_the_light_side(0, 0);
+}
diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
new file mode 100644
index 000000000..f71614a5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
@@ -0,0 +1,10 @@
+error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
+ --> $DIR/duplicate_underscore_argument.rs:4:23
+ |
+LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
+ | ^^^^^
+ |
+ = note: `-D clippy::duplicate-underscore-argument` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/duration_subsec.fixed b/src/tools/clippy/tests/ui/duration_subsec.fixed
new file mode 100644
index 000000000..d92b8998e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/duration_subsec.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+#![allow(dead_code, clippy::needless_borrow)]
+#![warn(clippy::duration_subsec)]
+
+use std::time::Duration;
+
+fn main() {
+ let dur = Duration::new(5, 0);
+
+ let bad_millis_1 = dur.subsec_millis();
+ let bad_millis_2 = dur.subsec_millis();
+ let good_millis = dur.subsec_millis();
+ assert_eq!(bad_millis_1, good_millis);
+ assert_eq!(bad_millis_2, good_millis);
+
+ let bad_micros = dur.subsec_micros();
+ let good_micros = dur.subsec_micros();
+ assert_eq!(bad_micros, good_micros);
+
+ // Handle refs
+ let _ = (&dur).subsec_micros();
+
+ // Handle constants
+ const NANOS_IN_MICRO: u32 = 1_000;
+ let _ = dur.subsec_micros();
+
+ // Other literals aren't linted
+ let _ = dur.subsec_nanos() / 699;
+}
diff --git a/src/tools/clippy/tests/ui/duration_subsec.rs b/src/tools/clippy/tests/ui/duration_subsec.rs
new file mode 100644
index 000000000..08da80499
--- /dev/null
+++ b/src/tools/clippy/tests/ui/duration_subsec.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+#![allow(dead_code, clippy::needless_borrow)]
+#![warn(clippy::duration_subsec)]
+
+use std::time::Duration;
+
+fn main() {
+ let dur = Duration::new(5, 0);
+
+ let bad_millis_1 = dur.subsec_micros() / 1_000;
+ let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
+ let good_millis = dur.subsec_millis();
+ assert_eq!(bad_millis_1, good_millis);
+ assert_eq!(bad_millis_2, good_millis);
+
+ let bad_micros = dur.subsec_nanos() / 1_000;
+ let good_micros = dur.subsec_micros();
+ assert_eq!(bad_micros, good_micros);
+
+ // Handle refs
+ let _ = (&dur).subsec_nanos() / 1_000;
+
+ // Handle constants
+ const NANOS_IN_MICRO: u32 = 1_000;
+ let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
+
+ // Other literals aren't linted
+ let _ = dur.subsec_nanos() / 699;
+}
diff --git a/src/tools/clippy/tests/ui/duration_subsec.stderr b/src/tools/clippy/tests/ui/duration_subsec.stderr
new file mode 100644
index 000000000..cdbeff6a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/duration_subsec.stderr
@@ -0,0 +1,34 @@
+error: calling `subsec_millis()` is more concise than this calculation
+ --> $DIR/duration_subsec.rs:10:24
+ |
+LL | let bad_millis_1 = dur.subsec_micros() / 1_000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
+ |
+ = note: `-D clippy::duration-subsec` implied by `-D warnings`
+
+error: calling `subsec_millis()` is more concise than this calculation
+ --> $DIR/duration_subsec.rs:11:24
+ |
+LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
+
+error: calling `subsec_micros()` is more concise than this calculation
+ --> $DIR/duration_subsec.rs:16:22
+ |
+LL | let bad_micros = dur.subsec_nanos() / 1_000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
+
+error: calling `subsec_micros()` is more concise than this calculation
+ --> $DIR/duration_subsec.rs:21:13
+ |
+LL | let _ = (&dur).subsec_nanos() / 1_000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
+
+error: calling `subsec_micros()` is more concise than this calculation
+ --> $DIR/duration_subsec.rs:25:13
+ |
+LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/else_if_without_else.rs b/src/tools/clippy/tests/ui/else_if_without_else.rs
new file mode 100644
index 000000000..879b3ac39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/else_if_without_else.rs
@@ -0,0 +1,58 @@
+#![warn(clippy::all)]
+#![warn(clippy::else_if_without_else)]
+
+fn bla1() -> bool {
+ unimplemented!()
+}
+fn bla2() -> bool {
+ unimplemented!()
+}
+fn bla3() -> bool {
+ unimplemented!()
+}
+
+fn main() {
+ if bla1() {
+ println!("if");
+ }
+
+ if bla1() {
+ println!("if");
+ } else {
+ println!("else");
+ }
+
+ if bla1() {
+ println!("if");
+ } else if bla2() {
+ println!("else if");
+ } else {
+ println!("else")
+ }
+
+ if bla1() {
+ println!("if");
+ } else if bla2() {
+ println!("else if 1");
+ } else if bla3() {
+ println!("else if 2");
+ } else {
+ println!("else")
+ }
+
+ if bla1() {
+ println!("if");
+ } else if bla2() {
+ //~ ERROR else if without else
+ println!("else if");
+ }
+
+ if bla1() {
+ println!("if");
+ } else if bla2() {
+ println!("else if 1");
+ } else if bla3() {
+ //~ ERROR else if without else
+ println!("else if 2");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/else_if_without_else.stderr b/src/tools/clippy/tests/ui/else_if_without_else.stderr
new file mode 100644
index 000000000..6f47658cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/else_if_without_else.stderr
@@ -0,0 +1,27 @@
+error: `if` expression with an `else if`, but without a final `else`
+ --> $DIR/else_if_without_else.rs:45:12
+ |
+LL | } else if bla2() {
+ | ____________^
+LL | | //~ ERROR else if without else
+LL | | println!("else if");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::else-if-without-else` implied by `-D warnings`
+ = help: add an `else` block here
+
+error: `if` expression with an `else if`, but without a final `else`
+ --> $DIR/else_if_without_else.rs:54:12
+ |
+LL | } else if bla3() {
+ | ____________^
+LL | | //~ ERROR else if without else
+LL | | println!("else if 2");
+LL | | }
+ | |_____^
+ |
+ = help: add an `else` block here
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_drop.fixed b/src/tools/clippy/tests/ui/empty_drop.fixed
new file mode 100644
index 000000000..2e1b76846
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_drop.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+ fn drop(&mut self) {
+ println!("dropping bar!");
+ }
+}
+
+// should error
+struct Baz;
+
+
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_drop.rs b/src/tools/clippy/tests/ui/empty_drop.rs
new file mode 100644
index 000000000..75232b033
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_drop.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+impl Drop for Foo {
+ fn drop(&mut self) {}
+}
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+ fn drop(&mut self) {
+ println!("dropping bar!");
+ }
+}
+
+// should error
+struct Baz;
+
+impl Drop for Baz {
+ fn drop(&mut self) {
+ {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_drop.stderr b/src/tools/clippy/tests/ui/empty_drop.stderr
new file mode 100644
index 000000000..70f7880d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_drop.stderr
@@ -0,0 +1,22 @@
+error: empty drop implementation
+ --> $DIR/empty_drop.rs:8:1
+ |
+LL | / impl Drop for Foo {
+LL | | fn drop(&mut self) {}
+LL | | }
+ | |_^ help: try removing this impl
+ |
+ = note: `-D clippy::empty-drop` implied by `-D warnings`
+
+error: empty drop implementation
+ --> $DIR/empty_drop.rs:24:1
+ |
+LL | / impl Drop for Baz {
+LL | | fn drop(&mut self) {
+LL | | {}
+LL | | }
+LL | | }
+ | |_^ help: try removing this impl
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs
new file mode 100644
index 000000000..a2e5c13c4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_enum.rs
@@ -0,0 +1,7 @@
+#![allow(dead_code)]
+#![warn(clippy::empty_enum)]
+// Enable never type to test empty enum lint
+#![feature(never_type)]
+enum Empty {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enum.stderr
new file mode 100644
index 000000000..7125e5f60
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_enum.stderr
@@ -0,0 +1,11 @@
+error: enum with no variants
+ --> $DIR/empty_enum.rs:5:1
+ |
+LL | enum Empty {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::empty-enum` implied by `-D warnings`
+ = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs
new file mode 100644
index 000000000..386677352
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs
@@ -0,0 +1,7 @@
+#![allow(dead_code)]
+#![warn(clippy::empty_enum)]
+
+// `never_type` is not enabled; this test has no stderr file
+enum Empty {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
new file mode 100644
index 000000000..697412c00
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
@@ -0,0 +1,120 @@
+// aux-build:proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr)]
+#![allow(clippy::assertions_on_constants)]
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+// This should produce a warning
+#[crate_type = "lib"]
+
+/// some comment
+fn with_one_newline_and_comment() { assert!(true) }
+
+// This should not produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() { assert!(true) }
+
+
+// This should produce a warning
+#[crate_type = "lib"]
+
+fn with_one_newline() { assert!(true) }
+
+// This should produce a warning, too
+#[crate_type = "lib"]
+
+
+fn with_two_newlines() { assert!(true) }
+
+
+// This should produce a warning
+#[crate_type = "lib"]
+
+enum Baz {
+ One,
+ Two
+}
+
+// This should produce a warning
+#[crate_type = "lib"]
+
+struct Foo {
+ one: isize,
+ two: isize
+}
+
+// This should produce a warning
+#[crate_type = "lib"]
+
+mod foo {
+}
+
+/// This doc comment should not produce a warning
+
+/** This is also a doc comment and should not produce a warning
+ */
+
+// This should not produce a warning
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(missing_docs)]
+fn three_attributes() { assert!(true) }
+
+// This should not produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+ true
+}
+
+// This should not produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+ Bar1,
+
+ Bar2,
+
+ Bar3,
+
+ Bar4
+}
+
+// This should not produce a warning because the empty line is inside a block comment
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct S;
+
+// This should not produce a warning
+#[crate_type = "lib"]
+/* test */
+pub struct T;
+
+// This should not produce a warning
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[fake_async_trait]
+pub trait Bazz {
+ fn foo() -> Vec<u8> {
+ let _i = "";
+
+
+
+ vec![]
+ }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
new file mode 100644
index 000000000..594fca44a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
@@ -0,0 +1,54 @@
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:11:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | | /// some comment
+LL | | fn with_one_newline_and_comment() { assert!(true) }
+ | |_
+ |
+ = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
+
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:23:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | | fn with_one_newline() { assert!(true) }
+ | |_
+
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:28:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | |
+LL | | fn with_two_newlines() { assert!(true) }
+ | |_
+
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:35:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | | enum Baz {
+ | |_
+
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:43:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | | struct Foo {
+ | |_
+
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+ --> $DIR/empty_line_after_outer_attribute.rs:51:1
+ |
+LL | / #[crate_type = "lib"]
+LL | |
+LL | | mod foo {
+ | |_
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs
new file mode 100644
index 000000000..8fd7697eb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_loop.rs
@@ -0,0 +1,51 @@
+// aux-build:macro_rules.rs
+
+#![warn(clippy::empty_loop)]
+
+#[macro_use]
+extern crate macro_rules;
+
+fn should_trigger() {
+ loop {}
+ loop {
+ loop {}
+ }
+
+ 'outer: loop {
+ 'inner: loop {}
+ }
+}
+
+fn should_not_trigger() {
+ loop {
+ panic!("This is fine")
+ }
+ let ten_millis = std::time::Duration::from_millis(10);
+ loop {
+ std::thread::sleep(ten_millis)
+ }
+
+ #[allow(clippy::never_loop)]
+ 'outer: loop {
+ 'inner: loop {
+ break 'inner;
+ }
+ break 'outer;
+ }
+
+ // Make sure `allow` works for this lint
+ #[allow(clippy::empty_loop)]
+ loop {}
+
+ // We don't lint loops inside macros
+ macro_rules! foo {
+ () => {
+ loop {}
+ };
+ }
+
+ // We don't lint external macros
+ foofoo!()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr
new file mode 100644
index 000000000..555f3d3d8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_loop.stderr
@@ -0,0 +1,27 @@
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/empty_loop.rs:9:5
+ |
+LL | loop {}
+ | ^^^^^^^
+ |
+ = note: `-D clippy::empty-loop` implied by `-D warnings`
+ = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
+
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/empty_loop.rs:11:9
+ |
+LL | loop {}
+ | ^^^^^^^
+ |
+ = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
+
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/empty_loop.rs:15:9
+ |
+LL | 'inner: loop {}
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
new file mode 100644
index 000000000..235e0fc51
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
@@ -0,0 +1,27 @@
+// compile-flags: -Clink-arg=-nostartfiles
+// ignore-macos
+// ignore-windows
+
+#![warn(clippy::empty_loop)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+use core::panic::PanicInfo;
+
+#[start]
+fn main(argc: isize, argv: *const *const u8) -> isize {
+ // This should trigger the lint
+ loop {}
+}
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ // This should NOT trigger the lint
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {
+ // This should also trigger the lint
+ loop {}
+}
diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.stderr b/src/tools/clippy/tests/ui/empty_loop_no_std.stderr
new file mode 100644
index 000000000..520248fcb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_loop_no_std.stderr
@@ -0,0 +1,19 @@
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/empty_loop_no_std.rs:14:5
+ |
+LL | loop {}
+ | ^^^^^^^
+ |
+ = note: `-D clippy::empty-loop` implied by `-D warnings`
+ = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body
+
+error: empty `loop {}` wastes CPU cycles
+ --> $DIR/empty_loop_no_std.rs:26:5
+ |
+LL | loop {}
+ | ^^^^^^^
+ |
+ = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed
new file mode 100644
index 000000000..80f07603b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct; // should trigger lint
+struct MyEmptyTupleStruct; // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+ #[cfg(feature = "thisisneverenabled")]
+ field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+ field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs
new file mode 100644
index 000000000..1d1ed4c76
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct {} // should trigger lint
+struct MyEmptyTupleStruct(); // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+ #[cfg(feature = "thisisneverenabled")]
+ field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+ field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr b/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr
new file mode 100644
index 000000000..0308cb557
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr
@@ -0,0 +1,19 @@
+error: found empty brackets on struct declaration
+ --> $DIR/empty_structs_with_brackets.rs:5:25
+ |
+LL | pub struct MyEmptyStruct {} // should trigger lint
+ | ^^^
+ |
+ = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings`
+ = help: remove the brackets
+
+error: found empty brackets on struct declaration
+ --> $DIR/empty_structs_with_brackets.rs:6:26
+ |
+LL | struct MyEmptyTupleStruct(); // should trigger lint
+ | ^^^
+ |
+ = help: remove the brackets
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed
new file mode 100644
index 000000000..e43635abc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.fixed
@@ -0,0 +1,154 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::arch::asm;
+use std::collections::HashMap;
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+ ($map:expr, $key:expr, $val:expr) => {
+ $map.insert($key, $val)
+ };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+ // or_insert(v)
+ m.entry(k).or_insert(v);
+
+ // semicolon on insert, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ if true {
+ v
+ } else {
+ v2
+ }
+ });
+
+ // semicolon on if, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ if true {
+ v
+ } else {
+ v2
+ }
+ });
+
+ // early return, use if let
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ if true {
+ e.insert(v);
+ } else {
+ e.insert(v2);
+ return;
+ }
+ }
+
+ // use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ foo();
+ v
+ });
+
+ // semicolon on insert and match, use or_insert_with(..)
+ m.entry(k).or_insert_with(|| {
+ match 0 {
+ 1 if true => {
+ v
+ },
+ _ => {
+ v2
+ },
+ }
+ });
+
+ // one branch doesn't insert, use if let
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ match 0 {
+ 0 => foo(),
+ _ => {
+ e.insert(v2);
+ },
+ };
+ }
+
+ // use or_insert_with
+ m.entry(k).or_insert_with(|| {
+ foo();
+ match 0 {
+ 0 if false => {
+ v
+ },
+ 1 => {
+ foo();
+ v
+ },
+ 2 | 3 => {
+ for _ in 0..2 {
+ foo();
+ }
+ if true {
+ v
+ } else {
+ v2
+ }
+ },
+ _ => {
+ v2
+ },
+ }
+ });
+
+ // ok, insert in loop
+ if !m.contains_key(&k) {
+ for _ in 0..2 {
+ m.insert(k, v);
+ }
+ }
+
+ // macro_expansion test, use or_insert(..)
+ m.entry(m!(k)).or_insert_with(|| m!(v));
+
+ // ok, map used before insertion
+ if !m.contains_key(&k) {
+ let _ = m.len();
+ m.insert(k, v);
+ }
+
+ // ok, inline asm
+ if !m.contains_key(&k) {
+ unsafe { asm!("nop") }
+ m.insert(k, v);
+ }
+
+ // ok, different keys.
+ if !m.contains_key(&k) {
+ m.insert(k2, v);
+ }
+
+ // ok, different maps
+ if !m.contains_key(&k) {
+ m2.insert(k, v);
+ }
+
+ // ok, insert in macro
+ if !m.contains_key(&k) {
+ insert!(m, k, v);
+ }
+
+ // or_insert_with. Partial move of a local declared in the closure is ok.
+ m.entry(k).or_insert_with(|| {
+ let x = (String::new(), String::new());
+ let _ = x.0;
+ v
+ });
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs
new file mode 100644
index 000000000..d999b3b7d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.rs
@@ -0,0 +1,158 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::arch::asm;
+use std::collections::HashMap;
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+ ($map:expr, $key:expr, $val:expr) => {
+ $map.insert($key, $val)
+ };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+ // or_insert(v)
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ }
+
+ // semicolon on insert, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+ }
+
+ // semicolon on if, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v)
+ } else {
+ m.insert(k, v2)
+ };
+ }
+
+ // early return, use if let
+ if !m.contains_key(&k) {
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ return;
+ }
+ }
+
+ // use or_insert_with(..)
+ if !m.contains_key(&k) {
+ foo();
+ m.insert(k, v);
+ }
+
+ // semicolon on insert and match, use or_insert_with(..)
+ if !m.contains_key(&k) {
+ match 0 {
+ 1 if true => {
+ m.insert(k, v);
+ },
+ _ => {
+ m.insert(k, v2);
+ },
+ };
+ }
+
+ // one branch doesn't insert, use if let
+ if !m.contains_key(&k) {
+ match 0 {
+ 0 => foo(),
+ _ => {
+ m.insert(k, v2);
+ },
+ };
+ }
+
+ // use or_insert_with
+ if !m.contains_key(&k) {
+ foo();
+ match 0 {
+ 0 if false => {
+ m.insert(k, v);
+ },
+ 1 => {
+ foo();
+ m.insert(k, v);
+ },
+ 2 | 3 => {
+ for _ in 0..2 {
+ foo();
+ }
+ if true {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ };
+ },
+ _ => {
+ m.insert(k, v2);
+ },
+ }
+ }
+
+ // ok, insert in loop
+ if !m.contains_key(&k) {
+ for _ in 0..2 {
+ m.insert(k, v);
+ }
+ }
+
+ // macro_expansion test, use or_insert(..)
+ if !m.contains_key(&m!(k)) {
+ m.insert(m!(k), m!(v));
+ }
+
+ // ok, map used before insertion
+ if !m.contains_key(&k) {
+ let _ = m.len();
+ m.insert(k, v);
+ }
+
+ // ok, inline asm
+ if !m.contains_key(&k) {
+ unsafe { asm!("nop") }
+ m.insert(k, v);
+ }
+
+ // ok, different keys.
+ if !m.contains_key(&k) {
+ m.insert(k2, v);
+ }
+
+ // ok, different maps
+ if !m.contains_key(&k) {
+ m2.insert(k, v);
+ }
+
+ // ok, insert in macro
+ if !m.contains_key(&k) {
+ insert!(m, k, v);
+ }
+
+ // or_insert_with. Partial move of a local declared in the closure is ok.
+ if !m.contains_key(&k) {
+ let x = (String::new(), String::new());
+ let _ = x.0;
+ m.insert(k, v);
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.stderr b/src/tools/clippy/tests/ui/entry.stderr
new file mode 100644
index 000000000..2ef996652
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.stderr
@@ -0,0 +1,217 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:24:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^ help: try this: `m.entry(k).or_insert(v);`
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:29:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + if true {
+LL + v
+LL + } else {
+LL + v2
+LL + }
+LL + });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:38:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v)
+LL | | } else {
+LL | | m.insert(k, v2)
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + if true {
+LL + v
+LL + } else {
+LL + v2
+LL + }
+LL + });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:47:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | if true {
+LL | | m.insert(k, v);
+LL | | } else {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL + if true {
+LL + e.insert(v);
+LL + } else {
+LL + e.insert(v2);
+LL + return;
+LL + }
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:57:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + foo();
+LL + v
+LL + });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:63:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | match 0 {
+LL | | 1 if true => {
+LL | | m.insert(k, v);
+... |
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + match 0 {
+LL + 1 if true => {
+LL + v
+LL + },
+LL + _ => {
+LL + v2
+LL + },
+LL + }
+LL + });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:75:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | match 0 {
+LL | | 0 => foo(),
+LL | | _ => {
+... |
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL + match 0 {
+LL + 0 => foo(),
+LL + _ => {
+LL + e.insert(v2);
+LL + },
+LL + };
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:85:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | match 0 {
+LL | | 0 if false => {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + foo();
+LL + match 0 {
+LL + 0 if false => {
+LL + v
+LL + },
+LL + 1 => {
+LL + foo();
+LL + v
+LL + },
+LL + 2 | 3 => {
+LL + for _ in 0..2 {
+LL + foo();
+LL + }
+LL + if true {
+LL + v
+LL + } else {
+LL + v2
+LL + }
+LL + },
+LL + _ => {
+LL + v2
+LL + },
+LL + }
+LL + });
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:119:5
+ |
+LL | / if !m.contains_key(&m!(k)) {
+LL | | m.insert(m!(k), m!(v));
+LL | | }
+ | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry.rs:151:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | let x = (String::new(), String::new());
+LL | | let _ = x.0;
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ m.entry(k).or_insert_with(|| {
+LL + let x = (String::new(), String::new());
+LL + let _ = x.0;
+LL + v
+LL + });
+ |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/entry_btree.fixed b/src/tools/clippy/tests/ui/entry_btree.fixed
new file mode 100644
index 000000000..949791045
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::map_entry)]
+#![allow(dead_code)]
+
+use std::collections::BTreeMap;
+
+fn foo() {}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
+ // insert then do something, use if let
+ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+ e.insert(v);
+ foo();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_btree.rs b/src/tools/clippy/tests/ui/entry_btree.rs
new file mode 100644
index 000000000..080c1d959
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::map_entry)]
+#![allow(dead_code)]
+
+use std::collections::BTreeMap;
+
+fn foo() {}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
+ // insert then do something, use if let
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ foo();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_btree.stderr b/src/tools/clippy/tests/ui/entry_btree.stderr
new file mode 100644
index 000000000..5c6fcdf1a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.stderr
@@ -0,0 +1,20 @@
+error: usage of `contains_key` followed by `insert` on a `BTreeMap`
+ --> $DIR/entry_btree.rs:12:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | foo();
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+ |
+LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+LL + e.insert(v);
+LL + foo();
+LL + }
+ |
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/entry_with_else.fixed b/src/tools/clippy/tests/ui/entry_with_else.fixed
new file mode 100644
index 000000000..2332fa631
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.fixed
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v2);
+ }
+ }
+
+ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ e.insert(v);
+ } else {
+ foo();
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ }
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ e.insert(v2);
+ }
+ }
+
+ match m.entry(k) {
+ std::collections::hash_map::Entry::Occupied(mut e) => {
+ if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+ }
+ std::collections::hash_map::Entry::Vacant(e) => {
+ e.insert(v);
+ None
+ }
+ };
+
+ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+ foo();
+ Some(e.insert(v))
+ } else {
+ None
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.rs b/src/tools/clippy/tests/ui/entry_with_else.rs
new file mode 100644
index 000000000..2ff0c038e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.rs
@@ -0,0 +1,60 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+ ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ foo();
+ }
+
+ if !m.contains_key(&k) {
+ foo();
+ } else {
+ m.insert(k, v);
+ }
+
+ if !m.contains_key(&k) {
+ m.insert(k, v);
+ } else {
+ m.insert(k, v2);
+ }
+
+ if m.contains_key(&k) {
+ if true { m.insert(k, v) } else { m.insert(k, v2) }
+ } else {
+ m.insert(k, v)
+ };
+
+ if m.contains_key(&k) {
+ foo();
+ m.insert(k, v)
+ } else {
+ None
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.stderr b/src/tools/clippy/tests/ui/entry_with_else.stderr
new file mode 100644
index 000000000..e0f6671b4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.stderr
@@ -0,0 +1,151 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:16:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+ |
+LL ~ match m.entry(k) {
+LL + std::collections::hash_map::Entry::Vacant(e) => {
+LL + e.insert(v);
+LL + }
+LL + std::collections::hash_map::Entry::Occupied(mut e) => {
+LL + e.insert(v2);
+LL + }
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:22:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ match m.entry(k) {
+LL + std::collections::hash_map::Entry::Occupied(mut e) => {
+LL + e.insert(v);
+LL + }
+LL + std::collections::hash_map::Entry::Vacant(e) => {
+LL + e.insert(v2);
+LL + }
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:28:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | foo();
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL + e.insert(v);
+LL + } else {
+LL + foo();
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:34:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | foo();
+LL | | } else {
+LL | | m.insert(k, v);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL + e.insert(v);
+LL + } else {
+LL + foo();
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:40:5
+ |
+LL | / if !m.contains_key(&k) {
+LL | | m.insert(k, v);
+LL | | } else {
+LL | | m.insert(k, v2);
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ match m.entry(k) {
+LL + std::collections::hash_map::Entry::Vacant(e) => {
+LL + e.insert(v);
+LL + }
+LL + std::collections::hash_map::Entry::Occupied(mut e) => {
+LL + e.insert(v2);
+LL + }
+LL + }
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:46:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | if true { m.insert(k, v) } else { m.insert(k, v2) }
+LL | | } else {
+LL | | m.insert(k, v)
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL ~ match m.entry(k) {
+LL + std::collections::hash_map::Entry::Occupied(mut e) => {
+LL + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+LL + }
+LL + std::collections::hash_map::Entry::Vacant(e) => {
+LL + e.insert(v);
+LL + None
+LL + }
+LL ~ };
+ |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+ --> $DIR/entry_with_else.rs:52:5
+ |
+LL | / if m.contains_key(&k) {
+LL | | foo();
+LL | | m.insert(k, v)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL + foo();
+LL + Some(e.insert(v))
+LL + } else {
+LL + None
+LL ~ };
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
new file mode 100644
index 000000000..7d6842f5b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
@@ -0,0 +1,50 @@
+// ignore-x86
+
+#![warn(clippy::enum_clike_unportable_variant)]
+#![allow(unused, non_upper_case_globals)]
+
+#[repr(usize)]
+enum NonPortable {
+ X = 0x1_0000_0000,
+ Y = 0,
+ Z = 0x7FFF_FFFF,
+ A = 0xFFFF_FFFF,
+}
+
+enum NonPortableNoHint {
+ X = 0x1_0000_0000,
+ Y = 0,
+ Z = 0x7FFF_FFFF,
+ A = 0xFFFF_FFFF,
+}
+
+#[repr(isize)]
+enum NonPortableSigned {
+ X = -1,
+ Y = 0x7FFF_FFFF,
+ Z = 0xFFFF_FFFF,
+ A = 0x1_0000_0000,
+ B = i32::MIN as isize,
+ C = (i32::MIN as isize) - 1,
+}
+
+enum NonPortableSignedNoHint {
+ X = -1,
+ Y = 0x7FFF_FFFF,
+ Z = 0xFFFF_FFFF,
+ A = 0x1_0000_0000,
+}
+
+#[repr(usize)]
+enum NonPortable2 {
+ X = <usize as Trait>::Number,
+ Y = 0,
+}
+
+trait Trait {
+ const Number: usize = 0x1_0000_0000;
+}
+
+impl Trait for usize {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
new file mode 100644
index 000000000..5935eea5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
@@ -0,0 +1,58 @@
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:8:5
+ |
+LL | X = 0x1_0000_0000,
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:15:5
+ |
+LL | X = 0x1_0000_0000,
+ | ^^^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:18:5
+ |
+LL | A = 0xFFFF_FFFF,
+ | ^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:25:5
+ |
+LL | Z = 0xFFFF_FFFF,
+ | ^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:26:5
+ |
+LL | A = 0x1_0000_0000,
+ | ^^^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:28:5
+ |
+LL | C = (i32::MIN as isize) - 1,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:34:5
+ |
+LL | Z = 0xFFFF_FFFF,
+ | ^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:35:5
+ |
+LL | A = 0x1_0000_0000,
+ | ^^^^^^^^^^^^^^^^^
+
+error: C-like enum variant discriminant is not portable to 32-bit targets
+ --> $DIR/enum_clike_unportable_variant.rs:40:5
+ |
+LL | X = <usize as Trait>::Number,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/enum_glob_use.fixed b/src/tools/clippy/tests/ui/enum_glob_use.fixed
new file mode 100644
index 000000000..a98216758
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_glob_use.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![warn(clippy::enum_glob_use)]
+#![allow(unused)]
+#![warn(unused_imports)]
+
+use std::cmp::Ordering::Less;
+
+enum Enum {
+ Foo,
+}
+
+use self::Enum::Foo;
+
+mod in_fn_test {
+ fn blarg() {
+ use crate::Enum::Foo;
+
+ let _ = Foo;
+ }
+}
+
+mod blurg {
+ pub use std::cmp::Ordering::*; // ok, re-export
+}
+
+fn main() {
+ let _ = Foo;
+ let _ = Less;
+}
diff --git a/src/tools/clippy/tests/ui/enum_glob_use.rs b/src/tools/clippy/tests/ui/enum_glob_use.rs
new file mode 100644
index 000000000..5d929c973
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_glob_use.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![warn(clippy::enum_glob_use)]
+#![allow(unused)]
+#![warn(unused_imports)]
+
+use std::cmp::Ordering::*;
+
+enum Enum {
+ Foo,
+}
+
+use self::Enum::*;
+
+mod in_fn_test {
+ fn blarg() {
+ use crate::Enum::*;
+
+ let _ = Foo;
+ }
+}
+
+mod blurg {
+ pub use std::cmp::Ordering::*; // ok, re-export
+}
+
+fn main() {
+ let _ = Foo;
+ let _ = Less;
+}
diff --git a/src/tools/clippy/tests/ui/enum_glob_use.stderr b/src/tools/clippy/tests/ui/enum_glob_use.stderr
new file mode 100644
index 000000000..69531aed3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_glob_use.stderr
@@ -0,0 +1,22 @@
+error: usage of wildcard import for enum variants
+ --> $DIR/enum_glob_use.rs:7:5
+ |
+LL | use std::cmp::Ordering::*;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::cmp::Ordering::Less`
+ |
+ = note: `-D clippy::enum-glob-use` implied by `-D warnings`
+
+error: usage of wildcard import for enum variants
+ --> $DIR/enum_glob_use.rs:13:5
+ |
+LL | use self::Enum::*;
+ | ^^^^^^^^^^^^^ help: try: `self::Enum::Foo`
+
+error: usage of wildcard import for enum variants
+ --> $DIR/enum_glob_use.rs:17:13
+ |
+LL | use crate::Enum::*;
+ | ^^^^^^^^^^^^^^ help: try: `crate::Enum::Foo`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs
new file mode 100644
index 000000000..efed12ee2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_variants.rs
@@ -0,0 +1,182 @@
+#![warn(clippy::enum_variant_names)]
+#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
+
+enum FakeCallType {
+ CALL,
+ CREATE,
+}
+
+enum FakeCallType2 {
+ CALL,
+ CREATELL,
+}
+
+enum Foo {
+ cFoo,
+ cBar,
+ cBaz,
+}
+
+enum Fooo {
+ cFoo, // no error, threshold is 3 variants by default
+ cBar,
+}
+
+enum Food {
+ FoodGood,
+ FoodMiddle,
+ FoodBad,
+}
+
+enum Stuff {
+ StuffBad, // no error
+}
+
+enum BadCallType {
+ CallTypeCall,
+ CallTypeCreate,
+ CallTypeDestroy,
+}
+
+enum TwoCallType {
+ // no error
+ CallTypeCall,
+ CallTypeCreate,
+}
+
+enum Consts {
+ ConstantInt,
+ ConstantCake,
+ ConstantLie,
+}
+
+enum Two {
+ // no error here
+ ConstantInt,
+ ConstantInfer,
+}
+
+enum Something {
+ CCall,
+ CCreate,
+ CCryogenize,
+}
+
+enum Seal {
+ With,
+ Without,
+}
+
+enum Seall {
+ With,
+ WithOut,
+ Withbroken,
+}
+
+enum Sealll {
+ With,
+ WithOut,
+}
+
+enum Seallll {
+ WithOutCake,
+ WithOutTea,
+ WithOut,
+}
+
+enum NonCaps {
+ Prefix的,
+ PrefixTea,
+ PrefixCake,
+}
+
+pub enum PubSeall {
+ WithOutCake,
+ WithOutTea,
+ WithOut,
+}
+
+#[allow(clippy::enum_variant_names)]
+pub mod allowed {
+ pub enum PubAllowed {
+ SomeThis,
+ SomeThat,
+ SomeOtherWhat,
+ }
+}
+
+// should not lint
+enum Pat {
+ Foo,
+ Bar,
+ Path,
+}
+
+// should not lint
+enum N {
+ Pos,
+ Neg,
+ Float,
+}
+
+// should not lint
+enum Peek {
+ Peek1,
+ Peek2,
+ Peek3,
+}
+
+// should not lint
+pub enum NetworkLayer {
+ Layer2,
+ Layer3,
+}
+
+// should lint suggesting `IData`, not only `Data` (see #4639)
+enum IDataRequest {
+ PutIData(String),
+ GetIData(String),
+ DeleteUnpubIData(String),
+}
+
+enum HIDataRequest {
+ PutHIData(String),
+ GetHIData(String),
+ DeleteUnpubHIData(String),
+}
+
+enum North {
+ Normal,
+ NoLeft,
+ NoRight,
+}
+
+// #8324
+enum Phase {
+ PreLookup,
+ Lookup,
+ PostLookup,
+}
+
+mod issue9018 {
+ enum DoLint {
+ _TypeCreate,
+ _TypeRead,
+ _TypeUpdate,
+ _TypeDestroy,
+ }
+
+ enum DoLintToo {
+ _CreateType,
+ _UpdateType,
+ _DeleteType,
+ }
+
+ enum DoNotLint {
+ _Foo,
+ _Bar,
+ _Baz,
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr
new file mode 100644
index 000000000..7342aff80
--- /dev/null
+++ b/src/tools/clippy/tests/ui/enum_variants.stderr
@@ -0,0 +1,149 @@
+error: variant name ends with the enum's name
+ --> $DIR/enum_variants.rs:15:5
+ |
+LL | cFoo,
+ | ^^^^
+ |
+ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
+
+error: all variants have the same prefix: `c`
+ --> $DIR/enum_variants.rs:14:1
+ |
+LL | / enum Foo {
+LL | | cFoo,
+LL | | cBar,
+LL | | cBaz,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:26:5
+ |
+LL | FoodGood,
+ | ^^^^^^^^
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:27:5
+ |
+LL | FoodMiddle,
+ | ^^^^^^^^^^
+
+error: variant name starts with the enum's name
+ --> $DIR/enum_variants.rs:28:5
+ |
+LL | FoodBad,
+ | ^^^^^^^
+
+error: all variants have the same prefix: `Food`
+ --> $DIR/enum_variants.rs:25:1
+ |
+LL | / enum Food {
+LL | | FoodGood,
+LL | | FoodMiddle,
+LL | | FoodBad,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `CallType`
+ --> $DIR/enum_variants.rs:35:1
+ |
+LL | / enum BadCallType {
+LL | | CallTypeCall,
+LL | | CallTypeCreate,
+LL | | CallTypeDestroy,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `Constant`
+ --> $DIR/enum_variants.rs:47:1
+ |
+LL | / enum Consts {
+LL | | ConstantInt,
+LL | | ConstantCake,
+LL | | ConstantLie,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `C`
+ --> $DIR/enum_variants.rs:59:1
+ |
+LL | / enum Something {
+LL | | CCall,
+LL | | CCreate,
+LL | | CCryogenize,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `WithOut`
+ --> $DIR/enum_variants.rs:81:1
+ |
+LL | / enum Seallll {
+LL | | WithOutCake,
+LL | | WithOutTea,
+LL | | WithOut,
+LL | | }
+ | |_^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `IData`
+ --> $DIR/enum_variants.rs:136:1
+ |
+LL | / enum IDataRequest {
+LL | | PutIData(String),
+LL | | GetIData(String),
+LL | | DeleteUnpubIData(String),
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `HIData`
+ --> $DIR/enum_variants.rs:142:1
+ |
+LL | / enum HIDataRequest {
+LL | | PutHIData(String),
+LL | | GetHIData(String),
+LL | | DeleteUnpubHIData(String),
+LL | | }
+ | |_^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same prefix: `_Type`
+ --> $DIR/enum_variants.rs:162:5
+ |
+LL | / enum DoLint {
+LL | | _TypeCreate,
+LL | | _TypeRead,
+LL | | _TypeUpdate,
+LL | | _TypeDestroy,
+LL | | }
+ | |_____^
+ |
+ = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `Type`
+ --> $DIR/enum_variants.rs:169:5
+ |
+LL | / enum DoLintToo {
+LL | | _CreateType,
+LL | | _UpdateType,
+LL | | _DeleteType,
+LL | | }
+ | |_____^
+ |
+ = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.rs b/src/tools/clippy/tests/ui/eprint_with_newline.rs
new file mode 100644
index 000000000..8df32649a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.rs
@@ -0,0 +1,49 @@
+#![allow(clippy::print_literal)]
+#![warn(clippy::print_with_newline)]
+
+fn main() {
+ eprint!("Hello\n");
+ eprint!("Hello {}\n", "world");
+ eprint!("Hello {} {}\n", "world", "#2");
+ eprint!("{}\n", 1265);
+ eprint!("\n");
+
+ // these are all fine
+ eprint!("");
+ eprint!("Hello");
+ eprintln!("Hello");
+ eprintln!("Hello\n");
+ eprintln!("Hello {}\n", "world");
+ eprint!("Issue\n{}", 1265);
+ eprint!("{}", 1265);
+ eprint!("\n{}", 1275);
+ eprint!("\n\n");
+ eprint!("like eof\n\n");
+ eprint!("Hello {} {}\n\n", "world", "#2");
+ eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ eprintln!("\nbla\n\n"); // #3126
+
+ // Escaping
+ eprint!("\\n"); // #3514
+ eprint!("\\\n"); // should fail
+ eprint!("\\\\n");
+
+ // Raw strings
+ eprint!(r"\n"); // #3778
+
+ // Literal newlines should also fail
+ eprint!(
+ "
+"
+ );
+ eprint!(
+ r"
+"
+ );
+
+ // Don't warn on CRLF (#4208)
+ eprint!("\r\n");
+ eprint!("foo\r\n");
+ eprint!("\\r\n"); //~ ERROR
+ eprint!("foo\rbar\n") // ~ ERROR
+}
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.stderr b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
new file mode 100644
index 000000000..f137787bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
@@ -0,0 +1,129 @@
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:5:5
+ |
+LL | eprint!("Hello/n");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::print-with-newline` implied by `-D warnings`
+help: use `eprintln!` instead
+ |
+LL - eprint!("Hello/n");
+LL + eprintln!("Hello");
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:6:5
+ |
+LL | eprint!("Hello {}/n", "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("Hello {}/n", "world");
+LL + eprintln!("Hello {}", "world");
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:7:5
+ |
+LL | eprint!("Hello {} {}/n", "world", "#2");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("Hello {} {}/n", "world", "#2");
+LL + eprintln!("Hello {} {}", "world", "#2");
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:8:5
+ |
+LL | eprint!("{}/n", 1265);
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("{}/n", 1265);
+LL + eprintln!("{}", 1265);
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:9:5
+ |
+LL | eprint!("/n");
+ | ^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("/n");
+LL + eprintln!();
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:28:5
+ |
+LL | eprint!("//n"); // should fail
+ | ^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("//n"); // should fail
+LL + eprintln!("/"); // should fail
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:35:5
+ |
+LL | / eprint!(
+LL | | "
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `eprintln!` instead
+ |
+LL ~ eprintln!(
+LL ~ ""
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:39:5
+ |
+LL | / eprint!(
+LL | | r"
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `eprintln!` instead
+ |
+LL ~ eprintln!(
+LL ~ r""
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:47:5
+ |
+LL | eprint!("/r/n"); //~ ERROR
+ | ^^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("/r/n"); //~ ERROR
+LL + eprintln!("/r"); //~ ERROR
+ |
+
+error: using `eprint!()` with a format string that ends in a single newline
+ --> $DIR/eprint_with_newline.rs:48:5
+ |
+LL | eprint!("foo/rbar/n") // ~ ERROR
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `eprintln!` instead
+ |
+LL - eprint!("foo/rbar/n") // ~ ERROR
+LL + eprintln!("foo/rbar") // ~ ERROR
+ |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/eq_op.rs b/src/tools/clippy/tests/ui/eq_op.rs
new file mode 100644
index 000000000..422f94865
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eq_op.rs
@@ -0,0 +1,108 @@
+// compile-flags: --test
+
+#![warn(clippy::eq_op)]
+#![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)]
+
+fn main() {
+ // simple values and comparisons
+ let _ = 1 == 1;
+ let _ = "no" == "no";
+ // even though I agree that no means no ;-)
+ let _ = false != false;
+ let _ = 1.5 < 1.5;
+ let _ = 1u64 >= 1u64;
+
+ // casts, methods, parentheses
+ let _ = (1u32 as u64) & (1u32 as u64);
+ #[rustfmt::skip]
+ {
+ let _ = 1 ^ ((((((1))))));
+ };
+
+ // unary and binary operators
+ let _ = (-(2) < -(2));
+ let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
+
+ // various other things
+ let _ = ([1] != [1]);
+ let _ = ((1, 2) != (1, 2));
+ let _ = vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
+
+ // const folding
+ let _ = 1 + 1 == 2;
+ let _ = 1 - 1 == 0;
+
+ let _ = 1 - 1;
+ let _ = 1 / 1;
+ let _ = true && true;
+
+ let _ = true || true;
+
+ let a: u32 = 0;
+ let b: u32 = 0;
+
+ let _ = a == b && b == a;
+ let _ = a != b && b != a;
+ let _ = a < b && b > a;
+ let _ = a <= b && b >= a;
+
+ let mut a = vec![1];
+ let _ = a == a;
+ let _ = 2 * a.len() == 2 * a.len(); // ok, functions
+ let _ = a.pop() == a.pop(); // ok, functions
+
+ check_ignore_macro();
+
+ // named constants
+ const A: u32 = 10;
+ const B: u32 = 10;
+ const C: u32 = A / B; // ok, different named constants
+ const D: u32 = A / A;
+}
+
+macro_rules! check_if_named_foo {
+ ($expression:expr) => {
+ if stringify!($expression) == "foo" {
+ println!("foo!");
+ } else {
+ println!("not foo.");
+ }
+ };
+}
+
+macro_rules! bool_macro {
+ ($expression:expr) => {
+ true
+ };
+}
+
+fn check_ignore_macro() {
+ check_if_named_foo!(foo);
+ // checks if the lint ignores macros with `!` operator
+ let _ = !bool_macro!(1) && !bool_macro!("");
+}
+
+struct Nested {
+ inner: ((i32,), (i32,), (i32,)),
+}
+
+fn check_nested(n1: &Nested, n2: &Nested) -> bool {
+ // `n2.inner.0.0` mistyped as `n1.inner.0.0`
+ (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+}
+
+#[test]
+fn eq_op_shouldnt_trigger_in_tests() {
+ let a = 1;
+ let result = a + 1 == 1 + a;
+ assert!(result);
+}
+
+#[test]
+fn eq_op_macros_shouldnt_trigger_in_tests() {
+ let a = 1;
+ let b = 2;
+ assert_eq!(a, a);
+ assert_eq!(a + b, b + a);
+}
diff --git a/src/tools/clippy/tests/ui/eq_op.stderr b/src/tools/clippy/tests/ui/eq_op.stderr
new file mode 100644
index 000000000..313ceed2b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eq_op.stderr
@@ -0,0 +1,172 @@
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:8:13
+ |
+LL | let _ = 1 == 1;
+ | ^^^^^^
+ |
+ = note: `-D clippy::eq-op` implied by `-D warnings`
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:9:13
+ |
+LL | let _ = "no" == "no";
+ | ^^^^^^^^^^^^
+
+error: equal expressions as operands to `!=`
+ --> $DIR/eq_op.rs:11:13
+ |
+LL | let _ = false != false;
+ | ^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `<`
+ --> $DIR/eq_op.rs:12:13
+ |
+LL | let _ = 1.5 < 1.5;
+ | ^^^^^^^^^
+
+error: equal expressions as operands to `>=`
+ --> $DIR/eq_op.rs:13:13
+ |
+LL | let _ = 1u64 >= 1u64;
+ | ^^^^^^^^^^^^
+
+error: equal expressions as operands to `&`
+ --> $DIR/eq_op.rs:16:13
+ |
+LL | let _ = (1u32 as u64) & (1u32 as u64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `^`
+ --> $DIR/eq_op.rs:19:17
+ |
+LL | let _ = 1 ^ ((((((1))))));
+ | ^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `<`
+ --> $DIR/eq_op.rs:23:13
+ |
+LL | let _ = (-(2) < -(2));
+ | ^^^^^^^^^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:24:13
+ |
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `&`
+ --> $DIR/eq_op.rs:24:14
+ |
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `&`
+ --> $DIR/eq_op.rs:24:35
+ |
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:25:13
+ |
+LL | let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `!=`
+ --> $DIR/eq_op.rs:28:13
+ |
+LL | let _ = ([1] != [1]);
+ | ^^^^^^^^^^^^
+
+error: equal expressions as operands to `!=`
+ --> $DIR/eq_op.rs:29:13
+ |
+LL | let _ = ((1, 2) != (1, 2));
+ | ^^^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:33:13
+ |
+LL | let _ = 1 + 1 == 2;
+ | ^^^^^^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:34:13
+ |
+LL | let _ = 1 - 1 == 0;
+ | ^^^^^^^^^^
+
+error: equal expressions as operands to `-`
+ --> $DIR/eq_op.rs:34:13
+ |
+LL | let _ = 1 - 1 == 0;
+ | ^^^^^
+
+error: equal expressions as operands to `-`
+ --> $DIR/eq_op.rs:36:13
+ |
+LL | let _ = 1 - 1;
+ | ^^^^^
+
+error: equal expressions as operands to `/`
+ --> $DIR/eq_op.rs:37:13
+ |
+LL | let _ = 1 / 1;
+ | ^^^^^
+
+error: equal expressions as operands to `&&`
+ --> $DIR/eq_op.rs:38:13
+ |
+LL | let _ = true && true;
+ | ^^^^^^^^^^^^
+
+error: equal expressions as operands to `||`
+ --> $DIR/eq_op.rs:40:13
+ |
+LL | let _ = true || true;
+ | ^^^^^^^^^^^^
+
+error: equal expressions as operands to `&&`
+ --> $DIR/eq_op.rs:45:13
+ |
+LL | let _ = a == b && b == a;
+ | ^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `&&`
+ --> $DIR/eq_op.rs:46:13
+ |
+LL | let _ = a != b && b != a;
+ | ^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `&&`
+ --> $DIR/eq_op.rs:47:13
+ |
+LL | let _ = a < b && b > a;
+ | ^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `&&`
+ --> $DIR/eq_op.rs:48:13
+ |
+LL | let _ = a <= b && b >= a;
+ | ^^^^^^^^^^^^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:51:13
+ |
+LL | let _ = a == a;
+ | ^^^^^^
+
+error: equal expressions as operands to `/`
+ --> $DIR/eq_op.rs:61:20
+ |
+LL | const D: u32 = A / A;
+ | ^^^^^
+
+error: equal expressions as operands to `==`
+ --> $DIR/eq_op.rs:92:5
+ |
+LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
+
diff --git a/src/tools/clippy/tests/ui/eq_op_macros.rs b/src/tools/clippy/tests/ui/eq_op_macros.rs
new file mode 100644
index 000000000..6b5b31a1a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eq_op_macros.rs
@@ -0,0 +1,56 @@
+#![warn(clippy::eq_op)]
+
+// lint also in macro definition
+macro_rules! assert_in_macro_def {
+ () => {
+ let a = 42;
+ assert_eq!(a, a);
+ assert_ne!(a, a);
+ debug_assert_eq!(a, a);
+ debug_assert_ne!(a, a);
+ };
+}
+
+// lint identical args in assert-like macro invocations (see #3574)
+fn main() {
+ assert_in_macro_def!();
+
+ let a = 1;
+ let b = 2;
+
+ // lint identical args in `assert_eq!`
+ assert_eq!(a, a);
+ assert_eq!(a + 1, a + 1);
+ // ok
+ assert_eq!(a, b);
+ assert_eq!(a, a + 1);
+ assert_eq!(a + 1, b + 1);
+
+ // lint identical args in `assert_ne!`
+ assert_ne!(a, a);
+ assert_ne!(a + 1, a + 1);
+ // ok
+ assert_ne!(a, b);
+ assert_ne!(a, a + 1);
+ assert_ne!(a + 1, b + 1);
+
+ // lint identical args in `debug_assert_eq!`
+ debug_assert_eq!(a, a);
+ debug_assert_eq!(a + 1, a + 1);
+ // ok
+ debug_assert_eq!(a, b);
+ debug_assert_eq!(a, a + 1);
+ debug_assert_eq!(a + 1, b + 1);
+
+ // lint identical args in `debug_assert_ne!`
+ debug_assert_ne!(a, a);
+ debug_assert_ne!(a + 1, a + 1);
+ // ok
+ debug_assert_ne!(a, b);
+ debug_assert_ne!(a, a + 1);
+ debug_assert_ne!(a + 1, b + 1);
+
+ let my_vec = vec![1; 5];
+ let mut my_iter = my_vec.iter();
+ assert_ne!(my_iter.next(), my_iter.next());
+}
diff --git a/src/tools/clippy/tests/ui/eq_op_macros.stderr b/src/tools/clippy/tests/ui/eq_op_macros.stderr
new file mode 100644
index 000000000..cd9f1826e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eq_op_macros.stderr
@@ -0,0 +1,95 @@
+error: identical args used in this `assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:7:20
+ |
+LL | assert_eq!(a, a);
+ | ^^^^
+...
+LL | assert_in_macro_def!();
+ | ---------------------- in this macro invocation
+ |
+ = note: `-D clippy::eq-op` implied by `-D warnings`
+ = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: identical args used in this `assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:8:20
+ |
+LL | assert_ne!(a, a);
+ | ^^^^
+...
+LL | assert_in_macro_def!();
+ | ---------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: identical args used in this `debug_assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:9:26
+ |
+LL | debug_assert_eq!(a, a);
+ | ^^^^
+...
+LL | assert_in_macro_def!();
+ | ---------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: identical args used in this `debug_assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:10:26
+ |
+LL | debug_assert_ne!(a, a);
+ | ^^^^
+...
+LL | assert_in_macro_def!();
+ | ---------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: identical args used in this `assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:22:16
+ |
+LL | assert_eq!(a, a);
+ | ^^^^
+
+error: identical args used in this `assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:23:16
+ |
+LL | assert_eq!(a + 1, a + 1);
+ | ^^^^^^^^^^^^
+
+error: identical args used in this `assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:30:16
+ |
+LL | assert_ne!(a, a);
+ | ^^^^
+
+error: identical args used in this `assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:31:16
+ |
+LL | assert_ne!(a + 1, a + 1);
+ | ^^^^^^^^^^^^
+
+error: identical args used in this `debug_assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:38:22
+ |
+LL | debug_assert_eq!(a, a);
+ | ^^^^
+
+error: identical args used in this `debug_assert_eq!` macro call
+ --> $DIR/eq_op_macros.rs:39:22
+ |
+LL | debug_assert_eq!(a + 1, a + 1);
+ | ^^^^^^^^^^^^
+
+error: identical args used in this `debug_assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:46:22
+ |
+LL | debug_assert_ne!(a, a);
+ | ^^^^
+
+error: identical args used in this `debug_assert_ne!` macro call
+ --> $DIR/eq_op_macros.rs:47:22
+ |
+LL | debug_assert_ne!(a + 1, a + 1);
+ | ^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed
new file mode 100644
index 000000000..687efdada
--- /dev/null
+++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed
@@ -0,0 +1,84 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
+#![warn(clippy::equatable_if_let)]
+
+#[macro_use]
+extern crate macro_rules;
+
+use std::cmp::Ordering;
+
+#[derive(PartialEq)]
+enum Enum {
+ TupleVariant(i32, u64),
+ RecordVariant { a: i64, b: u32 },
+ UnitVariant,
+ Recursive(Struct),
+}
+
+#[derive(PartialEq)]
+struct Struct {
+ a: i32,
+ b: bool,
+}
+
+enum NotPartialEq {
+ A,
+ B,
+}
+
+enum NotStructuralEq {
+ A,
+ B,
+}
+
+impl PartialEq for NotStructuralEq {
+ fn eq(&self, _: &NotStructuralEq) -> bool {
+ false
+ }
+}
+
+fn main() {
+ let a = 2;
+ let b = 3;
+ let c = Some(2);
+ let d = Struct { a: 2, b: false };
+ let e = Enum::UnitVariant;
+ let f = NotPartialEq::A;
+ let g = NotStructuralEq::A;
+
+ // true
+
+ if a == 2 {}
+ if a.cmp(&b) == Ordering::Greater {}
+ if c == Some(2) {}
+ if d == (Struct { a: 2, b: false }) {}
+ if e == Enum::TupleVariant(32, 64) {}
+ if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
+ if e == Enum::UnitVariant {}
+ if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
+
+ // false
+
+ if let 2 | 3 = a {}
+ if let x @ 2 = a {}
+ if let Some(3 | 4) = c {}
+ if let Struct { a, b: false } = d {}
+ if let Struct { a: 2, b: x } = d {}
+ if let NotPartialEq::A = f {}
+ if g == NotStructuralEq::A {}
+ if let Some(NotPartialEq::A) = Some(f) {}
+ if Some(g) == Some(NotStructuralEq::A) {}
+
+ macro_rules! m1 {
+ (x) => {
+ "abc"
+ };
+ }
+ if "abc" == m1!(x) {
+ println!("OK");
+ }
+
+ equatable_if_let!(a);
+}
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs
new file mode 100644
index 000000000..8c467d14d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/equatable_if_let.rs
@@ -0,0 +1,84 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
+#![warn(clippy::equatable_if_let)]
+
+#[macro_use]
+extern crate macro_rules;
+
+use std::cmp::Ordering;
+
+#[derive(PartialEq)]
+enum Enum {
+ TupleVariant(i32, u64),
+ RecordVariant { a: i64, b: u32 },
+ UnitVariant,
+ Recursive(Struct),
+}
+
+#[derive(PartialEq)]
+struct Struct {
+ a: i32,
+ b: bool,
+}
+
+enum NotPartialEq {
+ A,
+ B,
+}
+
+enum NotStructuralEq {
+ A,
+ B,
+}
+
+impl PartialEq for NotStructuralEq {
+ fn eq(&self, _: &NotStructuralEq) -> bool {
+ false
+ }
+}
+
+fn main() {
+ let a = 2;
+ let b = 3;
+ let c = Some(2);
+ let d = Struct { a: 2, b: false };
+ let e = Enum::UnitVariant;
+ let f = NotPartialEq::A;
+ let g = NotStructuralEq::A;
+
+ // true
+
+ if let 2 = a {}
+ if let Ordering::Greater = a.cmp(&b) {}
+ if let Some(2) = c {}
+ if let Struct { a: 2, b: false } = d {}
+ if let Enum::TupleVariant(32, 64) = e {}
+ if let Enum::RecordVariant { a: 64, b: 32 } = e {}
+ if let Enum::UnitVariant = e {}
+ if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
+
+ // false
+
+ if let 2 | 3 = a {}
+ if let x @ 2 = a {}
+ if let Some(3 | 4) = c {}
+ if let Struct { a, b: false } = d {}
+ if let Struct { a: 2, b: x } = d {}
+ if let NotPartialEq::A = f {}
+ if let NotStructuralEq::A = g {}
+ if let Some(NotPartialEq::A) = Some(f) {}
+ if let Some(NotStructuralEq::A) = Some(g) {}
+
+ macro_rules! m1 {
+ (x) => {
+ "abc"
+ };
+ }
+ if let m1!(x) = "abc" {
+ println!("OK");
+ }
+
+ equatable_if_let!(a);
+}
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr
new file mode 100644
index 000000000..9c4c3cc36
--- /dev/null
+++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr
@@ -0,0 +1,70 @@
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:53:8
+ |
+LL | if let 2 = a {}
+ | ^^^^^^^^^ help: try: `a == 2`
+ |
+ = note: `-D clippy::equatable-if-let` implied by `-D warnings`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:54:8
+ |
+LL | if let Ordering::Greater = a.cmp(&b) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:55:8
+ |
+LL | if let Some(2) = c {}
+ | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:56:8
+ |
+LL | if let Struct { a: 2, b: false } = d {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:57:8
+ |
+LL | if let Enum::TupleVariant(32, 64) = e {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:58:8
+ |
+LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:59:8
+ |
+LL | if let Enum::UnitVariant = e {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:60:8
+ |
+LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:70:8
+ |
+LL | if let NotStructuralEq::A = g {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:72:8
+ |
+LL | if let Some(NotStructuralEq::A) = Some(g) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
+
+error: this pattern matching can be expressed using equality
+ --> $DIR/equatable_if_let.rs:79:8
+ |
+LL | if let m1!(x) = "abc" {
+ | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs
new file mode 100644
index 000000000..ae2fad008
--- /dev/null
+++ b/src/tools/clippy/tests/ui/erasing_op.rs
@@ -0,0 +1,43 @@
+struct Length(u8);
+struct Meter;
+
+impl core::ops::Mul<Meter> for u8 {
+ type Output = Length;
+ fn mul(self, _: Meter) -> Length {
+ Length(self)
+ }
+}
+
+#[derive(Clone, Default, PartialEq, Eq, Hash)]
+struct Vec1 {
+ x: i32,
+}
+
+impl core::ops::Mul<Vec1> for i32 {
+ type Output = Vec1;
+ fn mul(self, mut right: Vec1) -> Vec1 {
+ right.x *= self;
+ right
+ }
+}
+
+impl core::ops::Mul<i32> for Vec1 {
+ type Output = Vec1;
+ fn mul(mut self, right: i32) -> Vec1 {
+ self.x *= right;
+ self
+ }
+}
+
+#[allow(clippy::no_effect)]
+#[warn(clippy::erasing_op)]
+fn main() {
+ let x: u8 = 0;
+
+ x * 0;
+ 0 & x;
+ 0 / x;
+ 0 * Meter; // no error: Output type is different from the non-zero argument
+ 0 * Vec1 { x: 5 };
+ Vec1 { x: 5 } * 0;
+}
diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr
new file mode 100644
index 000000000..165ed9bfe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/erasing_op.stderr
@@ -0,0 +1,34 @@
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/erasing_op.rs:37:5
+ |
+LL | x * 0;
+ | ^^^^^
+ |
+ = note: `-D clippy::erasing-op` implied by `-D warnings`
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/erasing_op.rs:38:5
+ |
+LL | 0 & x;
+ | ^^^^^
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/erasing_op.rs:39:5
+ |
+LL | 0 / x;
+ | ^^^^^
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/erasing_op.rs:41:5
+ |
+LL | 0 * Vec1 { x: 5 };
+ | ^^^^^^^^^^^^^^^^^
+
+error: this operation will always return zero. This is likely not the intended outcome
+ --> $DIR/erasing_op.rs:42:5
+ |
+LL | Vec1 { x: 5 } * 0;
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/err_expect.fixed b/src/tools/clippy/tests/ui/err_expect.fixed
new file mode 100644
index 000000000..7e18d70ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/err_expect.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+ let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
+ test_debug.expect_err("Testing debug type");
+
+ let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
+ test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/src/tools/clippy/tests/ui/err_expect.rs b/src/tools/clippy/tests/ui/err_expect.rs
new file mode 100644
index 000000000..bf8c3c9fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/err_expect.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+ let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
+ test_debug.err().expect("Testing debug type");
+
+ let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
+ test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/src/tools/clippy/tests/ui/err_expect.stderr b/src/tools/clippy/tests/ui/err_expect.stderr
new file mode 100644
index 000000000..ffd97e00a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/err_expect.stderr
@@ -0,0 +1,10 @@
+error: called `.err().expect()` on a `Result` value
+ --> $DIR/err_expect.rs:10:16
+ |
+LL | test_debug.err().expect("Testing debug type");
+ | ^^^^^^^^^^^^ help: try: `expect_err`
+ |
+ = note: `-D clippy::err-expect` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed
new file mode 100644
index 000000000..f8d559bf2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eta.fixed
@@ -0,0 +1,305 @@
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::redundant_closure_call,
+ clippy::needless_pass_by_value,
+ clippy::option_map_unit_fn,
+ clippy::needless_borrow
+)]
+#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
+
+use std::path::{Path, PathBuf};
+
+macro_rules! mac {
+ () => {
+ foobar()
+ };
+}
+
+macro_rules! closure_mac {
+ () => {
+ |n| foo(n)
+ };
+}
+
+fn main() {
+ let a = Some(1u8).map(foo);
+ let c = Some(1u8).map(|a| {1+2; foo}(a));
+ true.then(|| mac!()); // don't lint function in macro expansion
+ Some(1).map(closure_mac!()); // don't lint closure in macro expansion
+ let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
+ let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
+ all(&[1, 2, 3], &&2, below); //is adjusted
+ unsafe {
+ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
+ }
+
+ // See #815
+ let e = Some(1u8).map(|a| divergent(a));
+ let e = Some(1u8).map(generic);
+ let e = Some(1u8).map(generic);
+ // See #515
+ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
+ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
+
+ // issue #7224
+ let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+}
+
+trait TestTrait {
+ fn trait_foo(self) -> bool;
+ fn trait_foo_ref(&self) -> bool;
+}
+
+struct TestStruct<'a> {
+ some_ref: &'a i32,
+}
+
+impl<'a> TestStruct<'a> {
+ fn foo(self) -> bool {
+ false
+ }
+ unsafe fn foo_unsafe(self) -> bool {
+ true
+ }
+}
+
+impl<'a> TestTrait for TestStruct<'a> {
+ fn trait_foo(self) -> bool {
+ false
+ }
+ fn trait_foo_ref(&self) -> bool {
+ false
+ }
+}
+
+impl<'a> std::ops::Deref for TestStruct<'a> {
+ type Target = char;
+ fn deref(&self) -> &char {
+ &'a'
+ }
+}
+
+fn test_redundant_closures_containing_method_calls() {
+ let i = 10;
+ let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
+ let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
+ let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
+ unsafe {
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
+ }
+ let e = Some("str").map(std::string::ToString::to_string);
+ let e = Some('a').map(char::to_uppercase);
+ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
+ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
+ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
+ let c = Some(TestStruct { some_ref: &i })
+ .as_ref()
+ .map(|c| c.to_ascii_uppercase());
+
+ fn test_different_borrow_levels<T>(t: &[&T])
+ where
+ T: TestTrait,
+ {
+ t.iter().filter(|x| x.trait_foo_ref());
+ t.iter().map(|x| x.trait_foo_ref());
+ }
+}
+
+struct Thunk<T>(Box<dyn FnMut() -> T>);
+
+impl<T> Thunk<T> {
+ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
+ let mut option = Some(f);
+ // This should not trigger redundant_closure (#1439)
+ Thunk(Box::new(move || option.take().unwrap()()))
+ }
+
+ fn unwrap(self) -> T {
+ let Thunk(mut f) = self;
+ f()
+ }
+}
+
+fn foobar() {
+ let thunk = Thunk::new(|| println!("Hello, world!"));
+ thunk.unwrap()
+}
+
+fn foo(_: u8) {}
+
+fn foo2(_: u8) -> u8 {
+ 1u8
+}
+
+fn all<X, F>(x: &[X], y: &X, f: F) -> bool
+where
+ F: Fn(&X, &X) -> bool,
+{
+ x.iter().all(|e| f(e, y))
+}
+
+fn below(x: &u8, y: &u8) -> bool {
+ x < y
+}
+
+unsafe fn unsafe_fn(_: u8) {}
+
+fn divergent(_: u8) -> ! {
+ unimplemented!()
+}
+
+fn generic<T>(_: T) -> u8 {
+ 0
+}
+
+fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
+ requires_fn_once(x);
+}
+fn requires_fn_once<T: FnOnce()>(_: T) {}
+
+fn test_redundant_closure_with_function_pointer() {
+ type FnPtrType = fn(u8);
+ let foo_ptr: FnPtrType = foo;
+ let a = Some(1u8).map(foo_ptr);
+}
+
+fn test_redundant_closure_with_another_closure() {
+ let closure = |a| println!("{}", a);
+ let a = Some(1u8).map(closure);
+}
+
+fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
+ // Currently f is called when result of make_lazy is called.
+ // If the closure is removed, f will be called when make_lazy itself is
+ // called. This changes semantics, so the closure must stay.
+ Box::new(move |x| f()(x))
+}
+
+fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
+ f(&mut "Hello".to_owned())
+}
+fn test_difference_in_mutability() {
+ call(|s| s.clone());
+}
+
+struct Bar;
+impl std::ops::Deref for Bar {
+ type Target = str;
+ fn deref(&self) -> &str {
+ "hi"
+ }
+}
+
+fn test_deref_with_trait_method() {
+ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
+}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(&mut add_to_res);
+ y.into_iter().for_each(&mut add_to_res);
+ z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(&mut closure);
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(in_loop);
+ }
+}
+
+fn late_bound_lifetimes() {
+ fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+ fn map_str<F>(thunk: F)
+ where
+ F: FnOnce(&str),
+ {
+ }
+
+ fn map_str_to_path<F>(thunk: F)
+ where
+ F: FnOnce(&str) -> &Path,
+ {
+ }
+ map_str(|s| take_asref_path(s));
+ map_str_to_path(|s| s.as_ref());
+}
+
+mod type_param_bound {
+ trait Trait {
+ fn fun();
+ }
+
+ fn take<T: 'static>(_: T) {}
+
+ fn test<X: Trait>() {
+ // don't lint, but it's questionable that rust requires a cast
+ take(|| X::fun());
+ take(X::fun as fn());
+ }
+}
+
+// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
+fn arc_fp() {
+ let rc = std::rc::Rc::new(|| 7);
+ let arc = std::sync::Arc::new(|n| n + 1);
+ let ref_arc = &std::sync::Arc::new(|_| 5);
+
+ true.then(|| rc());
+ (0..5).map(|n| arc(n));
+ Some(4).map(|n| ref_arc(n));
+}
+
+// #8460 Don't replace closures with params bounded as `ref`
+mod bind_by_ref {
+ struct A;
+ struct B;
+
+ impl From<&A> for B {
+ fn from(A: &A) -> Self {
+ B
+ }
+ }
+
+ fn test() {
+ // should not lint
+ Some(A).map(|a| B::from(&a));
+ // should not lint
+ Some(A).map(|ref a| B::from(a));
+ }
+}
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+ fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+ function_returning_unit(|x| std::process::exit(x));
+
+ fn arr() -> &'static [u8; 0] {
+ &[]
+ }
+ fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+ slice_fn(|| arr());
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7861
+fn box_dyn() {
+ fn f(_: impl Fn(usize) -> Box<dyn std::any::Any>) {}
+ f(|x| Box::new(x));
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/5939
+fn not_general_enough() {
+ fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {}
+ f(|path| std::fs::remove_file(path));
+}
diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs
new file mode 100644
index 000000000..f0fb55a1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eta.rs
@@ -0,0 +1,305 @@
+// run-rustfix
+
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::redundant_closure_call,
+ clippy::needless_pass_by_value,
+ clippy::option_map_unit_fn,
+ clippy::needless_borrow
+)]
+#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
+
+use std::path::{Path, PathBuf};
+
+macro_rules! mac {
+ () => {
+ foobar()
+ };
+}
+
+macro_rules! closure_mac {
+ () => {
+ |n| foo(n)
+ };
+}
+
+fn main() {
+ let a = Some(1u8).map(|a| foo(a));
+ let c = Some(1u8).map(|a| {1+2; foo}(a));
+ true.then(|| mac!()); // don't lint function in macro expansion
+ Some(1).map(closure_mac!()); // don't lint closure in macro expansion
+ let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
+ let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
+ all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+ unsafe {
+ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
+ }
+
+ // See #815
+ let e = Some(1u8).map(|a| divergent(a));
+ let e = Some(1u8).map(|a| generic(a));
+ let e = Some(1u8).map(generic);
+ // See #515
+ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
+ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
+
+ // issue #7224
+ let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
+}
+
+trait TestTrait {
+ fn trait_foo(self) -> bool;
+ fn trait_foo_ref(&self) -> bool;
+}
+
+struct TestStruct<'a> {
+ some_ref: &'a i32,
+}
+
+impl<'a> TestStruct<'a> {
+ fn foo(self) -> bool {
+ false
+ }
+ unsafe fn foo_unsafe(self) -> bool {
+ true
+ }
+}
+
+impl<'a> TestTrait for TestStruct<'a> {
+ fn trait_foo(self) -> bool {
+ false
+ }
+ fn trait_foo_ref(&self) -> bool {
+ false
+ }
+}
+
+impl<'a> std::ops::Deref for TestStruct<'a> {
+ type Target = char;
+ fn deref(&self) -> &char {
+ &'a'
+ }
+}
+
+fn test_redundant_closures_containing_method_calls() {
+ let i = 10;
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
+ let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
+ unsafe {
+ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
+ }
+ let e = Some("str").map(|s| s.to_string());
+ let e = Some('a').map(|s| s.to_uppercase());
+ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
+ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
+ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
+ let c = Some(TestStruct { some_ref: &i })
+ .as_ref()
+ .map(|c| c.to_ascii_uppercase());
+
+ fn test_different_borrow_levels<T>(t: &[&T])
+ where
+ T: TestTrait,
+ {
+ t.iter().filter(|x| x.trait_foo_ref());
+ t.iter().map(|x| x.trait_foo_ref());
+ }
+}
+
+struct Thunk<T>(Box<dyn FnMut() -> T>);
+
+impl<T> Thunk<T> {
+ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
+ let mut option = Some(f);
+ // This should not trigger redundant_closure (#1439)
+ Thunk(Box::new(move || option.take().unwrap()()))
+ }
+
+ fn unwrap(self) -> T {
+ let Thunk(mut f) = self;
+ f()
+ }
+}
+
+fn foobar() {
+ let thunk = Thunk::new(|| println!("Hello, world!"));
+ thunk.unwrap()
+}
+
+fn foo(_: u8) {}
+
+fn foo2(_: u8) -> u8 {
+ 1u8
+}
+
+fn all<X, F>(x: &[X], y: &X, f: F) -> bool
+where
+ F: Fn(&X, &X) -> bool,
+{
+ x.iter().all(|e| f(e, y))
+}
+
+fn below(x: &u8, y: &u8) -> bool {
+ x < y
+}
+
+unsafe fn unsafe_fn(_: u8) {}
+
+fn divergent(_: u8) -> ! {
+ unimplemented!()
+}
+
+fn generic<T>(_: T) -> u8 {
+ 0
+}
+
+fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
+ requires_fn_once(|| x());
+}
+fn requires_fn_once<T: FnOnce()>(_: T) {}
+
+fn test_redundant_closure_with_function_pointer() {
+ type FnPtrType = fn(u8);
+ let foo_ptr: FnPtrType = foo;
+ let a = Some(1u8).map(|a| foo_ptr(a));
+}
+
+fn test_redundant_closure_with_another_closure() {
+ let closure = |a| println!("{}", a);
+ let a = Some(1u8).map(|a| closure(a));
+}
+
+fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
+ // Currently f is called when result of make_lazy is called.
+ // If the closure is removed, f will be called when make_lazy itself is
+ // called. This changes semantics, so the closure must stay.
+ Box::new(move |x| f()(x))
+}
+
+fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
+ f(&mut "Hello".to_owned())
+}
+fn test_difference_in_mutability() {
+ call(|s| s.clone());
+}
+
+struct Bar;
+impl std::ops::Deref for Bar {
+ type Target = str;
+ fn deref(&self) -> &str {
+ "hi"
+ }
+}
+
+fn test_deref_with_trait_method() {
+ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
+}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(|x| add_to_res(x));
+ y.into_iter().for_each(|x| add_to_res(x));
+ z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(|n| closure(n));
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(|n| in_loop(n));
+ }
+}
+
+fn late_bound_lifetimes() {
+ fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+ fn map_str<F>(thunk: F)
+ where
+ F: FnOnce(&str),
+ {
+ }
+
+ fn map_str_to_path<F>(thunk: F)
+ where
+ F: FnOnce(&str) -> &Path,
+ {
+ }
+ map_str(|s| take_asref_path(s));
+ map_str_to_path(|s| s.as_ref());
+}
+
+mod type_param_bound {
+ trait Trait {
+ fn fun();
+ }
+
+ fn take<T: 'static>(_: T) {}
+
+ fn test<X: Trait>() {
+ // don't lint, but it's questionable that rust requires a cast
+ take(|| X::fun());
+ take(X::fun as fn());
+ }
+}
+
+// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
+fn arc_fp() {
+ let rc = std::rc::Rc::new(|| 7);
+ let arc = std::sync::Arc::new(|n| n + 1);
+ let ref_arc = &std::sync::Arc::new(|_| 5);
+
+ true.then(|| rc());
+ (0..5).map(|n| arc(n));
+ Some(4).map(|n| ref_arc(n));
+}
+
+// #8460 Don't replace closures with params bounded as `ref`
+mod bind_by_ref {
+ struct A;
+ struct B;
+
+ impl From<&A> for B {
+ fn from(A: &A) -> Self {
+ B
+ }
+ }
+
+ fn test() {
+ // should not lint
+ Some(A).map(|a| B::from(&a));
+ // should not lint
+ Some(A).map(|ref a| B::from(a));
+ }
+}
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+ fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+ function_returning_unit(|x| std::process::exit(x));
+
+ fn arr() -> &'static [u8; 0] {
+ &[]
+ }
+ fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+ slice_fn(|| arr());
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7861
+fn box_dyn() {
+ fn f(_: impl Fn(usize) -> Box<dyn std::any::Any>) {}
+ f(|x| Box::new(x));
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/5939
+fn not_general_enough() {
+ fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {}
+ f(|path| std::fs::remove_file(path));
+}
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
new file mode 100644
index 000000000..bf2e97e74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -0,0 +1,120 @@
+error: redundant closure
+ --> $DIR/eta.rs:28:27
+ |
+LL | let a = Some(1u8).map(|a| foo(a));
+ | ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
+ |
+ = note: `-D clippy::redundant-closure` implied by `-D warnings`
+
+error: redundant closure
+ --> $DIR/eta.rs:32:40
+ |
+LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
+ | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
+
+error: redundant closure
+ --> $DIR/eta.rs:33:35
+ |
+LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
+ | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
+
+error: redundant closure
+ --> $DIR/eta.rs:34:26
+ |
+LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+ | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
+
+error: redundant closure
+ --> $DIR/eta.rs:41:27
+ |
+LL | let e = Some(1u8).map(|a| generic(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
+
+error: redundant closure
+ --> $DIR/eta.rs:87:51
+ |
+LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
+ | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
+ |
+ = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
+
+error: redundant closure
+ --> $DIR/eta.rs:88:51
+ |
+LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
+
+error: redundant closure
+ --> $DIR/eta.rs:90:42
+ |
+LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
+ | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
+
+error: redundant closure
+ --> $DIR/eta.rs:94:29
+ |
+LL | let e = Some("str").map(|s| s.to_string());
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
+
+error: redundant closure
+ --> $DIR/eta.rs:95:27
+ |
+LL | let e = Some('a').map(|s| s.to_uppercase());
+ | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
+
+error: redundant closure
+ --> $DIR/eta.rs:97:65
+ |
+LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
+
+error: redundant closure
+ --> $DIR/eta.rs:160:22
+ |
+LL | requires_fn_once(|| x());
+ | ^^^^^^ help: replace the closure with the function itself: `x`
+
+error: redundant closure
+ --> $DIR/eta.rs:167:27
+ |
+LL | let a = Some(1u8).map(|a| foo_ptr(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
+
+error: redundant closure
+ --> $DIR/eta.rs:172:27
+ |
+LL | let a = Some(1u8).map(|a| closure(a));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
+
+error: redundant closure
+ --> $DIR/eta.rs:204:28
+ |
+LL | x.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:205:28
+ |
+LL | y.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:206:28
+ |
+LL | z.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:213:21
+ |
+LL | Some(1).map(|n| closure(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
+error: redundant closure
+ --> $DIR/eta.rs:217:21
+ |
+LL | Some(1).map(|n| in_loop(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed
new file mode 100644
index 000000000..b74bda182
--- /dev/null
+++ b/src/tools/clippy/tests/ui/excessive_precision.fixed
@@ -0,0 +1,69 @@
+// run-rustfix
+#![warn(clippy::excessive_precision)]
+#![allow(dead_code, unused_variables, clippy::print_literal)]
+
+fn main() {
+ // Consts
+ const GOOD32: f32 = 0.123_456;
+ const GOOD32_SM: f32 = 0.000_000_000_1;
+ const GOOD32_DOT: f32 = 10_000_000_000.0;
+ const GOOD32_EDGE: f32 = 1.000_000_8;
+ const GOOD64: f64 = 0.123_456_789_012;
+ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
+ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
+
+ const BAD32_1: f32 = 0.123_456_79_f32;
+ const BAD32_2: f32 = 0.123_456_79;
+ const BAD32_3: f32 = 0.1;
+ const BAD32_EDGE: f32 = 1.000_001;
+
+ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+ const BAD64_2: f64 = 0.123_456_789_012_345_67;
+ const BAD64_3: f64 = 0.1;
+
+ // Literal as param
+ println!("{:?}", 8.888_888_888_888_89);
+
+ // // TODO add inferred type tests for f32
+ // Locals
+ let good32: f32 = 0.123_456_f32;
+ let good32_2: f32 = 0.123_456;
+
+ let good64: f64 = 0.123_456_789_012;
+ let good64_suf: f64 = 0.123_456_789_012f64;
+ let good64_inf = 0.123_456_789_012;
+
+ let bad32: f32 = 1.123_456_8;
+ let bad32_suf: f32 = 1.123_456_8_f32;
+ let bad32_inf = 1.123_456_8_f32;
+
+ let bad64: f64 = 0.123_456_789_012_345_67;
+ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+ let bad64_inf = 0.123_456_789_012_345_67;
+
+ // Vectors
+ let good_vec32: Vec<f32> = vec![0.123_456];
+ let good_vec64: Vec<f64> = vec![0.123_456_789];
+
+ let bad_vec32: Vec<f32> = vec![0.123_456_79];
+ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
+
+ // Exponential float notation
+ let good_e32: f32 = 1e-10;
+ let bad_e32: f32 = 1.123_456_8e-10;
+
+ let good_bige32: f32 = 1E-10;
+ let bad_bige32: f32 = 1.123_456_8E-10;
+
+ // Inferred type
+ let good_inferred: f32 = 1f32 * 1_000_000_000.;
+
+ // issue #2840
+ let num = 0.000_000_000_01e-10f64;
+
+ // issue #7744
+ let _ = 2.225_073_858_507_201e-308_f64;
+
+ // issue #7745
+ let _ = 0_f64;
+}
diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs
new file mode 100644
index 000000000..6e84a71f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/excessive_precision.rs
@@ -0,0 +1,69 @@
+// run-rustfix
+#![warn(clippy::excessive_precision)]
+#![allow(dead_code, unused_variables, clippy::print_literal)]
+
+fn main() {
+ // Consts
+ const GOOD32: f32 = 0.123_456;
+ const GOOD32_SM: f32 = 0.000_000_000_1;
+ const GOOD32_DOT: f32 = 10_000_000_000.0;
+ const GOOD32_EDGE: f32 = 1.000_000_8;
+ const GOOD64: f64 = 0.123_456_789_012;
+ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
+ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
+
+ const BAD32_1: f32 = 0.123_456_789_f32;
+ const BAD32_2: f32 = 0.123_456_789;
+ const BAD32_3: f32 = 0.100_000_000_000_1;
+ const BAD32_EDGE: f32 = 1.000_000_9;
+
+ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+ const BAD64_2: f64 = 0.123_456_789_012_345_67;
+ const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
+
+ // Literal as param
+ println!("{:?}", 8.888_888_888_888_888_888_888);
+
+ // // TODO add inferred type tests for f32
+ // Locals
+ let good32: f32 = 0.123_456_f32;
+ let good32_2: f32 = 0.123_456;
+
+ let good64: f64 = 0.123_456_789_012;
+ let good64_suf: f64 = 0.123_456_789_012f64;
+ let good64_inf = 0.123_456_789_012;
+
+ let bad32: f32 = 1.123_456_789;
+ let bad32_suf: f32 = 1.123_456_789_f32;
+ let bad32_inf = 1.123_456_789_f32;
+
+ let bad64: f64 = 0.123_456_789_012_345_67;
+ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+ let bad64_inf = 0.123_456_789_012_345_67;
+
+ // Vectors
+ let good_vec32: Vec<f32> = vec![0.123_456];
+ let good_vec64: Vec<f64> = vec![0.123_456_789];
+
+ let bad_vec32: Vec<f32> = vec![0.123_456_789];
+ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
+
+ // Exponential float notation
+ let good_e32: f32 = 1e-10;
+ let bad_e32: f32 = 1.123_456_788_888e-10;
+
+ let good_bige32: f32 = 1E-10;
+ let bad_bige32: f32 = 1.123_456_788_888E-10;
+
+ // Inferred type
+ let good_inferred: f32 = 1f32 * 1_000_000_000.;
+
+ // issue #2840
+ let num = 0.000_000_000_01e-10f64;
+
+ // issue #7744
+ let _ = 2.225_073_858_507_201_1e-308_f64;
+
+ // issue #7745
+ let _ = 1.000_000_000_000_001e-324_f64;
+}
diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr
new file mode 100644
index 000000000..42d9d4de1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/excessive_precision.stderr
@@ -0,0 +1,94 @@
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:15:26
+ |
+LL | const BAD32_1: f32 = 0.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
+ |
+ = note: `-D clippy::excessive-precision` implied by `-D warnings`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:16:26
+ |
+LL | const BAD32_2: f32 = 0.123_456_789;
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:17:26
+ |
+LL | const BAD32_3: f32 = 0.100_000_000_000_1;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:18:29
+ |
+LL | const BAD32_EDGE: f32 = 1.000_000_9;
+ | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:22:26
+ |
+LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:25:22
+ |
+LL | println!("{:?}", 8.888_888_888_888_888_888_888);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:36:22
+ |
+LL | let bad32: f32 = 1.123_456_789;
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:37:26
+ |
+LL | let bad32_suf: f32 = 1.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:38:21
+ |
+LL | let bad32_inf = 1.123_456_789_f32;
+ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:48:36
+ |
+LL | let bad_vec32: Vec<f32> = vec![0.123_456_789];
+ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:49:36
+ |
+LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:53:24
+ |
+LL | let bad_e32: f32 = 1.123_456_788_888e-10;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:56:27
+ |
+LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:65:13
+ |
+LL | let _ = 2.225_073_858_507_201_1e-308_f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64`
+
+error: float has excessive precision
+ --> $DIR/excessive_precision.rs:68:13
+ |
+LL | let _ = 1.000_000_000_000_001e-324_f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64`
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/exhaustive_items.fixed b/src/tools/clippy/tests/ui/exhaustive_items.fixed
new file mode 100644
index 000000000..c209f5b4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exhaustive_items.fixed
@@ -0,0 +1,91 @@
+// run-rustfix
+
+#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
+#![allow(unused)]
+
+fn main() {
+ // nop
+}
+
+pub mod enums {
+ #[non_exhaustive]
+ pub enum Exhaustive {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ /// Some docs
+ #[repr(C)]
+ #[non_exhaustive]
+ pub enum ExhaustiveWithAttrs {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, already non_exhaustive
+ #[non_exhaustive]
+ pub enum NonExhaustive {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, private
+ enum ExhaustivePrivate {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, private
+ #[non_exhaustive]
+ enum NonExhaustivePrivate {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+}
+
+pub mod structs {
+ #[non_exhaustive]
+ pub struct Exhaustive {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, already non_exhaustive
+ #[non_exhaustive]
+ pub struct NonExhaustive {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, private fields
+ pub struct ExhaustivePrivateFieldTuple(u8);
+
+ // no warning, private fields
+ pub struct ExhaustivePrivateField {
+ pub foo: u8,
+ bar: String,
+ }
+
+ // no warning, private
+ struct ExhaustivePrivate {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, private
+ #[non_exhaustive]
+ struct NonExhaustivePrivate {
+ pub foo: u8,
+ pub bar: String,
+ }
+}
diff --git a/src/tools/clippy/tests/ui/exhaustive_items.rs b/src/tools/clippy/tests/ui/exhaustive_items.rs
new file mode 100644
index 000000000..6f59dbf2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exhaustive_items.rs
@@ -0,0 +1,88 @@
+// run-rustfix
+
+#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
+#![allow(unused)]
+
+fn main() {
+ // nop
+}
+
+pub mod enums {
+ pub enum Exhaustive {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ /// Some docs
+ #[repr(C)]
+ pub enum ExhaustiveWithAttrs {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, already non_exhaustive
+ #[non_exhaustive]
+ pub enum NonExhaustive {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, private
+ enum ExhaustivePrivate {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+
+ // no warning, private
+ #[non_exhaustive]
+ enum NonExhaustivePrivate {
+ Foo,
+ Bar,
+ Baz,
+ Quux(String),
+ }
+}
+
+pub mod structs {
+ pub struct Exhaustive {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, already non_exhaustive
+ #[non_exhaustive]
+ pub struct NonExhaustive {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, private fields
+ pub struct ExhaustivePrivateFieldTuple(u8);
+
+ // no warning, private fields
+ pub struct ExhaustivePrivateField {
+ pub foo: u8,
+ bar: String,
+ }
+
+ // no warning, private
+ struct ExhaustivePrivate {
+ pub foo: u8,
+ pub bar: String,
+ }
+
+ // no warning, private
+ #[non_exhaustive]
+ struct NonExhaustivePrivate {
+ pub foo: u8,
+ pub bar: String,
+ }
+}
diff --git a/src/tools/clippy/tests/ui/exhaustive_items.stderr b/src/tools/clippy/tests/ui/exhaustive_items.stderr
new file mode 100644
index 000000000..f46ebd477
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exhaustive_items.stderr
@@ -0,0 +1,61 @@
+error: exported enums should not be exhaustive
+ --> $DIR/exhaustive_items.rs:11:5
+ |
+LL | / pub enum Exhaustive {
+LL | | Foo,
+LL | | Bar,
+LL | | Baz,
+LL | | Quux(String),
+LL | | }
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/exhaustive_items.rs:3:9
+ |
+LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+help: try adding #[non_exhaustive]
+ |
+LL ~ #[non_exhaustive]
+LL ~ pub enum Exhaustive {
+ |
+
+error: exported enums should not be exhaustive
+ --> $DIR/exhaustive_items.rs:20:5
+ |
+LL | / pub enum ExhaustiveWithAttrs {
+LL | | Foo,
+LL | | Bar,
+LL | | Baz,
+LL | | Quux(String),
+LL | | }
+ | |_____^
+ |
+help: try adding #[non_exhaustive]
+ |
+LL ~ #[non_exhaustive]
+LL ~ pub enum ExhaustiveWithAttrs {
+ |
+
+error: exported structs should not be exhaustive
+ --> $DIR/exhaustive_items.rs:55:5
+ |
+LL | / pub struct Exhaustive {
+LL | | pub foo: u8,
+LL | | pub bar: String,
+LL | | }
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/exhaustive_items.rs:3:35
+ |
+LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try adding #[non_exhaustive]
+ |
+LL ~ #[non_exhaustive]
+LL ~ pub struct Exhaustive {
+ |
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/exit1.rs b/src/tools/clippy/tests/ui/exit1.rs
new file mode 100644
index 000000000..4eac6eb74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit1.rs
@@ -0,0 +1,15 @@
+#[warn(clippy::exit)]
+
+fn not_main() {
+ if true {
+ std::process::exit(4);
+ }
+}
+
+fn main() {
+ if true {
+ std::process::exit(2);
+ };
+ not_main();
+ std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/exit1.stderr b/src/tools/clippy/tests/ui/exit1.stderr
new file mode 100644
index 000000000..a8d3956aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit1.stderr
@@ -0,0 +1,10 @@
+error: usage of `process::exit`
+ --> $DIR/exit1.rs:5:9
+ |
+LL | std::process::exit(4);
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::exit` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/exit2.rs b/src/tools/clippy/tests/ui/exit2.rs
new file mode 100644
index 000000000..4b693ed70
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit2.rs
@@ -0,0 +1,13 @@
+#[warn(clippy::exit)]
+
+fn also_not_main() {
+ std::process::exit(3);
+}
+
+fn main() {
+ if true {
+ std::process::exit(2);
+ };
+ also_not_main();
+ std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/exit2.stderr b/src/tools/clippy/tests/ui/exit2.stderr
new file mode 100644
index 000000000..7263e156a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit2.stderr
@@ -0,0 +1,10 @@
+error: usage of `process::exit`
+ --> $DIR/exit2.rs:4:5
+ |
+LL | std::process::exit(3);
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::exit` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/exit3.rs b/src/tools/clippy/tests/ui/exit3.rs
new file mode 100644
index 000000000..9dc0e1015
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit3.rs
@@ -0,0 +1,8 @@
+#[warn(clippy::exit)]
+
+fn main() {
+ if true {
+ std::process::exit(2);
+ };
+ std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/expect.rs b/src/tools/clippy/tests/ui/expect.rs
new file mode 100644
index 000000000..1073acf6f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::expect_used)]
+
+fn expect_option() {
+ let opt = Some(0);
+ let _ = opt.expect("");
+}
+
+fn expect_result() {
+ let res: Result<u8, ()> = Ok(0);
+ let _ = res.expect("");
+}
+
+fn main() {
+ expect_option();
+ expect_result();
+}
diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr
new file mode 100644
index 000000000..9d3fc7df1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect.stderr
@@ -0,0 +1,19 @@
+error: used `expect()` on `an Option` value
+ --> $DIR/expect.rs:5:13
+ |
+LL | let _ = opt.expect("");
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::expect-used` implied by `-D warnings`
+ = help: if this value is an `None`, it will panic
+
+error: used `expect()` on `a Result` value
+ --> $DIR/expect.rs:10:13
+ |
+LL | let _ = res.expect("");
+ | ^^^^^^^^^^^^^^
+ |
+ = help: if this value is an `Err`, it will panic
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed
new file mode 100644
index 000000000..53e45d28b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed
@@ -0,0 +1,104 @@
+// run-rustfix
+
+#![warn(clippy::expect_fun_call)]
+#![allow(clippy::to_string_in_format_args)]
+
+/// Checks implementation of the `EXPECT_FUN_CALL` lint
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Self {
+ Foo
+ }
+
+ fn expect(&self, msg: &str) {
+ panic!("{}", msg)
+ }
+ }
+
+ let with_some = Some("value");
+ with_some.expect("error");
+
+ let with_none: Option<i32> = None;
+ with_none.expect("error");
+
+ let error_code = 123_i32;
+ let with_none_and_format: Option<i32> = None;
+ with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
+
+ let with_none_and_as_str: Option<i32> = None;
+ with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
+
+ let with_none_and_format_with_macro: Option<i32> = None;
+ with_none_and_format_with_macro.unwrap_or_else(|| panic!("Error {}: fake error", one!()));
+
+ let with_ok: Result<(), ()> = Ok(());
+ with_ok.expect("error");
+
+ let with_err: Result<(), ()> = Err(());
+ with_err.expect("error");
+
+ let error_code = 123_i32;
+ let with_err_and_format: Result<(), ()> = Err(());
+ with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
+
+ let with_err_and_as_str: Result<(), ()> = Err(());
+ with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
+
+ let with_dummy_type = Foo::new();
+ with_dummy_type.expect("another test string");
+
+ let with_dummy_type_and_format = Foo::new();
+ with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
+
+ let with_dummy_type_and_as_str = Foo::new();
+ with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+
+ //Issue #2937
+ Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2));
+
+ //Issue #2979 - this should not lint
+ {
+ let msg = "bar";
+ Some("foo").expect(msg);
+ }
+
+ {
+ fn get_string() -> String {
+ "foo".to_string()
+ }
+
+ fn get_static_str() -> &'static str {
+ "foo"
+ }
+
+ fn get_non_static_str(_: &u32) -> &str {
+ "foo"
+ }
+
+ Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
+ Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
+ Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
+
+ Some("foo").unwrap_or_else(|| { panic!("{}", get_static_str()) });
+ Some("foo").unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) });
+ }
+
+ //Issue #3839
+ Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2));
+
+ //Issue #4912 - the receiver is a &Option
+ {
+ let opt = Some(1);
+ let opt_ref = &opt;
+ opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs
new file mode 100644
index 000000000..22e530b80
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect_fun_call.rs
@@ -0,0 +1,104 @@
+// run-rustfix
+
+#![warn(clippy::expect_fun_call)]
+#![allow(clippy::to_string_in_format_args)]
+
+/// Checks implementation of the `EXPECT_FUN_CALL` lint
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Self {
+ Foo
+ }
+
+ fn expect(&self, msg: &str) {
+ panic!("{}", msg)
+ }
+ }
+
+ let with_some = Some("value");
+ with_some.expect("error");
+
+ let with_none: Option<i32> = None;
+ with_none.expect("error");
+
+ let error_code = 123_i32;
+ let with_none_and_format: Option<i32> = None;
+ with_none_and_format.expect(&format!("Error {}: fake error", error_code));
+
+ let with_none_and_as_str: Option<i32> = None;
+ with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+
+ let with_none_and_format_with_macro: Option<i32> = None;
+ with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
+
+ let with_ok: Result<(), ()> = Ok(());
+ with_ok.expect("error");
+
+ let with_err: Result<(), ()> = Err(());
+ with_err.expect("error");
+
+ let error_code = 123_i32;
+ let with_err_and_format: Result<(), ()> = Err(());
+ with_err_and_format.expect(&format!("Error {}: fake error", error_code));
+
+ let with_err_and_as_str: Result<(), ()> = Err(());
+ with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+
+ let with_dummy_type = Foo::new();
+ with_dummy_type.expect("another test string");
+
+ let with_dummy_type_and_format = Foo::new();
+ with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
+
+ let with_dummy_type_and_as_str = Foo::new();
+ with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+
+ //Issue #2937
+ Some("foo").expect(format!("{} {}", 1, 2).as_ref());
+
+ //Issue #2979 - this should not lint
+ {
+ let msg = "bar";
+ Some("foo").expect(msg);
+ }
+
+ {
+ fn get_string() -> String {
+ "foo".to_string()
+ }
+
+ fn get_static_str() -> &'static str {
+ "foo"
+ }
+
+ fn get_non_static_str(_: &u32) -> &str {
+ "foo"
+ }
+
+ Some("foo").expect(&get_string());
+ Some("foo").expect(get_string().as_ref());
+ Some("foo").expect(get_string().as_str());
+
+ Some("foo").expect(get_static_str());
+ Some("foo").expect(get_non_static_str(&0));
+ }
+
+ //Issue #3839
+ Some(true).expect(&format!("key {}, {}", 1, 2));
+
+ //Issue #4912 - the receiver is a &Option
+ {
+ let opt = Some(1);
+ let opt_ref = &opt;
+ opt_ref.expect(&format!("{:?}", opt_ref));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr
new file mode 100644
index 000000000..aca15935f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr
@@ -0,0 +1,82 @@
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:35:26
+ |
+LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
+ |
+ = note: `-D clippy::expect-fun-call` implied by `-D warnings`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:38:26
+ |
+LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:41:37
+ |
+LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:51:25
+ |
+LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:54:25
+ |
+LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:66:17
+ |
+LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:87:21
+ |
+LL | Some("foo").expect(&get_string());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:88:21
+ |
+LL | Some("foo").expect(get_string().as_ref());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:89:21
+ |
+LL | Some("foo").expect(get_string().as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:91:21
+ |
+LL | Some("foo").expect(get_static_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:92:21
+ |
+LL | Some("foo").expect(get_non_static_str(&0));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:96:16
+ |
+LL | Some(true).expect(&format!("key {}, {}", 1, 2));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:102:17
+ |
+LL | opt_ref.expect(&format!("{:?}", opt_ref));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs
new file mode 100644
index 000000000..28b37f96e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs
@@ -0,0 +1,142 @@
+// check-pass
+#![feature(lint_reasons)]
+//! This file tests the `#[expect]` attribute implementation for tool lints. The same
+//! file is used to test clippy and rustdoc. Any changes to this file should be synced
+//! to the other test files as well.
+//!
+//! Expectations:
+//! * rustc: only rustc lint expectations are emitted
+//! * clippy: rustc and Clippy's expectations are emitted
+//! * rustdoc: only rustdoc lint expectations are emitted
+//!
+//! This test can't cover every lint from Clippy, rustdoc and potentially other
+//! tools that will be developed. This therefore only tests a small subset of lints
+#![expect(rustdoc::missing_crate_level_docs)]
+
+mod rustc_ok {
+ //! See <https://doc.rust-lang.org/rustc/lints/index.html>
+
+ #[expect(dead_code)]
+ pub fn rustc_lints() {
+ let x = 42.0;
+
+ #[expect(illegal_floating_point_literal_pattern)]
+ match x {
+ 5.0 => {}
+ 6.0 => {}
+ _ => {}
+ }
+ }
+}
+
+mod rustc_warn {
+ //! See <https://doc.rust-lang.org/rustc/lints/index.html>
+
+ #[expect(dead_code)]
+ pub fn rustc_lints() {
+ let x = 42;
+
+ #[expect(illegal_floating_point_literal_pattern)]
+ match x {
+ 5 => {}
+ 6 => {}
+ _ => {}
+ }
+ }
+}
+
+pub mod rustdoc_ok {
+ //! See <https://doc.rust-lang.org/rustdoc/lints.html>
+
+ #[expect(rustdoc::broken_intra_doc_links)]
+ /// I want to link to [`Nonexistent`] but it doesn't exist!
+ pub fn foo() {}
+
+ #[expect(rustdoc::invalid_html_tags)]
+ /// <h1>
+ pub fn bar() {}
+
+ #[expect(rustdoc::bare_urls)]
+ /// http://example.org
+ pub fn baz() {}
+}
+
+pub mod rustdoc_warn {
+ //! See <https://doc.rust-lang.org/rustdoc/lints.html>
+
+ #[expect(rustdoc::broken_intra_doc_links)]
+ /// I want to link to [`bar`] but it doesn't exist!
+ pub fn foo() {}
+
+ #[expect(rustdoc::invalid_html_tags)]
+ /// <h1></h1>
+ pub fn bar() {}
+
+ #[expect(rustdoc::bare_urls)]
+ /// <http://example.org>
+ pub fn baz() {}
+}
+
+mod clippy_ok {
+ //! See <https://rust-lang.github.io/rust-clippy/master/index.html>
+
+ #[expect(clippy::almost_swapped)]
+ fn foo() {
+ let mut a = 0;
+ let mut b = 9;
+ a = b;
+ b = a;
+ }
+
+ #[expect(clippy::bytes_nth)]
+ fn bar() {
+ let _ = "Hello".bytes().nth(3);
+ }
+
+ #[expect(clippy::if_same_then_else)]
+ fn baz() {
+ let _ = if true { 42 } else { 42 };
+ }
+
+ #[expect(clippy::logic_bug)]
+ fn burger() {
+ let a = false;
+ let b = true;
+
+ if a && b || a {}
+ }
+}
+
+mod clippy_warn {
+ //! See <https://rust-lang.github.io/rust-clippy/master/index.html>
+
+ #[expect(clippy::almost_swapped)]
+ fn foo() {
+ let mut a = 0;
+ let mut b = 9;
+ a = b;
+ }
+
+ #[expect(clippy::bytes_nth)]
+ fn bar() {
+ let _ = "Hello".as_bytes().get(3);
+ }
+
+ #[expect(clippy::if_same_then_else)]
+ fn baz() {
+ let _ = if true { 33 } else { 42 };
+ }
+
+ #[expect(clippy::logic_bug)]
+ fn burger() {
+ let a = false;
+ let b = true;
+ let c = false;
+
+ if a && b || c {}
+ }
+}
+
+fn main() {
+ rustc_warn::rustc_lints();
+}
diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr
new file mode 100644
index 000000000..db29e85a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr
@@ -0,0 +1,40 @@
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:35:14
+ |
+LL | #[expect(dead_code)]
+ | ^^^^^^^^^
+ |
+ = note: `-D unfulfilled-lint-expectations` implied by `-D warnings`
+
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:39:18
+ |
+LL | #[expect(illegal_floating_point_literal_pattern)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:113:14
+ |
+LL | #[expect(clippy::almost_swapped)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:120:14
+ |
+LL | #[expect(clippy::bytes_nth)]
+ | ^^^^^^^^^^^^^^^^^
+
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:125:14
+ |
+LL | #[expect(clippy::if_same_then_else)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this lint expectation is unfulfilled
+ --> $DIR/expect_tool_lint_rfc_2383.rs:130:14
+ |
+LL | #[expect(clippy::logic_bug)]
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
new file mode 100644
index 000000000..a650fdc1f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
@@ -0,0 +1,218 @@
+// run-rustfix
+
+#![warn(clippy::explicit_auto_deref)]
+#![allow(
+ dead_code,
+ unused_braces,
+ clippy::borrowed_box,
+ clippy::needless_borrow,
+ clippy::needless_return,
+ clippy::ptr_arg,
+ clippy::redundant_field_names,
+ clippy::too_many_arguments,
+ clippy::borrow_deref_ref,
+ clippy::let_unit_value
+)]
+
+trait CallableStr {
+ type T: Fn(&str);
+ fn callable_str(&self) -> Self::T;
+}
+impl CallableStr for () {
+ type T = fn(&str);
+ fn callable_str(&self) -> Self::T {
+ fn f(_: &str) {}
+ f
+ }
+}
+impl CallableStr for i32 {
+ type T = <() as CallableStr>::T;
+ fn callable_str(&self) -> Self::T {
+ ().callable_str()
+ }
+}
+
+trait CallableT<U: ?Sized> {
+ type T: Fn(&U);
+ fn callable_t(&self) -> Self::T;
+}
+impl<U: ?Sized> CallableT<U> for () {
+ type T = fn(&U);
+ fn callable_t(&self) -> Self::T {
+ fn f<U: ?Sized>(_: &U) {}
+ f::<U>
+ }
+}
+impl<U: ?Sized> CallableT<U> for i32 {
+ type T = <() as CallableT<U>>::T;
+ fn callable_t(&self) -> Self::T {
+ ().callable_t()
+ }
+}
+
+fn f_str(_: &str) {}
+fn f_string(_: &String) {}
+fn f_t<T>(_: T) {}
+fn f_ref_t<T: ?Sized>(_: &T) {}
+
+fn f_str_t<T>(_: &str, _: T) {}
+
+fn f_box_t<T>(_: &Box<T>) {}
+
+extern "C" {
+ fn var(_: u32, ...);
+}
+
+fn main() {
+ let s = String::new();
+
+ let _: &str = &s;
+ let _ = &*s; // Don't lint. Inferred type would change.
+ let _: &_ = &*s; // Don't lint. Inferred type would change.
+
+ f_str(&s);
+ f_t(&*s); // Don't lint. Inferred type would change.
+ f_ref_t(&*s); // Don't lint. Inferred type would change.
+
+ f_str_t(&s, &*s); // Don't lint second param.
+
+ let b = Box::new(Box::new(Box::new(5)));
+ let _: &Box<i32> = &b;
+ let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
+
+ f_box_t(&**b); // Don't lint. Inferred type would change.
+
+ let c = |_x: &str| ();
+ c(&s);
+
+ let c = |_x| ();
+ c(&*s); // Don't lint. Inferred type would change.
+
+ fn _f(x: &String) -> &str {
+ x
+ }
+
+ fn _f1(x: &String) -> &str {
+ { x }
+ }
+
+ fn _f2(x: &String) -> &str {
+ { x }
+ }
+
+ fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
+ x
+ }
+
+ fn _f4(
+ x: String,
+ f1: impl Fn(&str),
+ f2: &dyn Fn(&str),
+ f3: fn(&str),
+ f4: impl CallableStr,
+ f5: <() as CallableStr>::T,
+ f6: <i32 as CallableStr>::T,
+ f7: &dyn CallableStr<T = fn(&str)>,
+ f8: impl CallableT<str>,
+ f9: <() as CallableT<str>>::T,
+ f10: <i32 as CallableT<str>>::T,
+ f11: &dyn CallableT<str, T = fn(&str)>,
+ ) {
+ f1(&x);
+ f2(&x);
+ f3(&x);
+ f4.callable_str()(&x);
+ f5(&x);
+ f6(&x);
+ f7.callable_str()(&x);
+ f8.callable_t()(&x);
+ f9(&x);
+ f10(&x);
+ f11.callable_t()(&x);
+ }
+
+ struct S1<'a>(&'a str);
+ let _ = S1(&s);
+
+ struct S2<'a> {
+ s: &'a str,
+ }
+ let _ = S2 { s: &s };
+
+ struct S3<'a, T: ?Sized>(&'a T);
+ let _ = S3(&*s); // Don't lint. Inferred type would change.
+
+ struct S4<'a, T: ?Sized> {
+ s: &'a T,
+ }
+ let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
+
+ enum E1<'a> {
+ S1(&'a str),
+ S2 { s: &'a str },
+ }
+ impl<'a> E1<'a> {
+ fn m1(s: &'a String) {
+ let _ = Self::S1(s);
+ let _ = Self::S2 { s: s };
+ }
+ }
+ let _ = E1::S1(&s);
+ let _ = E1::S2 { s: &s };
+
+ enum E2<'a, T: ?Sized> {
+ S1(&'a T),
+ S2 { s: &'a T },
+ }
+ let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
+ let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
+
+ let ref_s = &s;
+ let _: &String = &*ref_s; // Don't lint reborrow.
+ f_string(&*ref_s); // Don't lint reborrow.
+
+ struct S5 {
+ foo: u32,
+ }
+ let b = Box::new(Box::new(S5 { foo: 5 }));
+ let _ = b.foo;
+ let _ = b.foo;
+ let _ = b.foo;
+
+ struct S6 {
+ foo: S5,
+ }
+ impl core::ops::Deref for S6 {
+ type Target = S5;
+ fn deref(&self) -> &Self::Target {
+ &self.foo
+ }
+ }
+ let s6 = S6 { foo: S5 { foo: 5 } };
+ let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
+
+ let ref_str = &"foo";
+ let _ = f_str(ref_str);
+ let ref_ref_str = &ref_str;
+ let _ = f_str(ref_ref_str);
+
+ fn _f5(x: &u32) -> u32 {
+ if true {
+ *x
+ } else {
+ return *x;
+ }
+ }
+
+ f_str(&&ref_str); // `needless_borrow` will suggest removing both references
+ f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
+
+ let x = &&40;
+ unsafe {
+ var(0, &**x);
+ }
+
+ let s = &"str";
+ let _ = || return *s;
+ let _ = || -> &'static str { return s };
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
new file mode 100644
index 000000000..8f4f35257
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
@@ -0,0 +1,218 @@
+// run-rustfix
+
+#![warn(clippy::explicit_auto_deref)]
+#![allow(
+ dead_code,
+ unused_braces,
+ clippy::borrowed_box,
+ clippy::needless_borrow,
+ clippy::needless_return,
+ clippy::ptr_arg,
+ clippy::redundant_field_names,
+ clippy::too_many_arguments,
+ clippy::borrow_deref_ref,
+ clippy::let_unit_value
+)]
+
+trait CallableStr {
+ type T: Fn(&str);
+ fn callable_str(&self) -> Self::T;
+}
+impl CallableStr for () {
+ type T = fn(&str);
+ fn callable_str(&self) -> Self::T {
+ fn f(_: &str) {}
+ f
+ }
+}
+impl CallableStr for i32 {
+ type T = <() as CallableStr>::T;
+ fn callable_str(&self) -> Self::T {
+ ().callable_str()
+ }
+}
+
+trait CallableT<U: ?Sized> {
+ type T: Fn(&U);
+ fn callable_t(&self) -> Self::T;
+}
+impl<U: ?Sized> CallableT<U> for () {
+ type T = fn(&U);
+ fn callable_t(&self) -> Self::T {
+ fn f<U: ?Sized>(_: &U) {}
+ f::<U>
+ }
+}
+impl<U: ?Sized> CallableT<U> for i32 {
+ type T = <() as CallableT<U>>::T;
+ fn callable_t(&self) -> Self::T {
+ ().callable_t()
+ }
+}
+
+fn f_str(_: &str) {}
+fn f_string(_: &String) {}
+fn f_t<T>(_: T) {}
+fn f_ref_t<T: ?Sized>(_: &T) {}
+
+fn f_str_t<T>(_: &str, _: T) {}
+
+fn f_box_t<T>(_: &Box<T>) {}
+
+extern "C" {
+ fn var(_: u32, ...);
+}
+
+fn main() {
+ let s = String::new();
+
+ let _: &str = &*s;
+ let _ = &*s; // Don't lint. Inferred type would change.
+ let _: &_ = &*s; // Don't lint. Inferred type would change.
+
+ f_str(&*s);
+ f_t(&*s); // Don't lint. Inferred type would change.
+ f_ref_t(&*s); // Don't lint. Inferred type would change.
+
+ f_str_t(&*s, &*s); // Don't lint second param.
+
+ let b = Box::new(Box::new(Box::new(5)));
+ let _: &Box<i32> = &**b;
+ let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
+
+ f_box_t(&**b); // Don't lint. Inferred type would change.
+
+ let c = |_x: &str| ();
+ c(&*s);
+
+ let c = |_x| ();
+ c(&*s); // Don't lint. Inferred type would change.
+
+ fn _f(x: &String) -> &str {
+ &**x
+ }
+
+ fn _f1(x: &String) -> &str {
+ { &**x }
+ }
+
+ fn _f2(x: &String) -> &str {
+ &**{ x }
+ }
+
+ fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
+ &***x
+ }
+
+ fn _f4(
+ x: String,
+ f1: impl Fn(&str),
+ f2: &dyn Fn(&str),
+ f3: fn(&str),
+ f4: impl CallableStr,
+ f5: <() as CallableStr>::T,
+ f6: <i32 as CallableStr>::T,
+ f7: &dyn CallableStr<T = fn(&str)>,
+ f8: impl CallableT<str>,
+ f9: <() as CallableT<str>>::T,
+ f10: <i32 as CallableT<str>>::T,
+ f11: &dyn CallableT<str, T = fn(&str)>,
+ ) {
+ f1(&*x);
+ f2(&*x);
+ f3(&*x);
+ f4.callable_str()(&*x);
+ f5(&*x);
+ f6(&*x);
+ f7.callable_str()(&*x);
+ f8.callable_t()(&*x);
+ f9(&*x);
+ f10(&*x);
+ f11.callable_t()(&*x);
+ }
+
+ struct S1<'a>(&'a str);
+ let _ = S1(&*s);
+
+ struct S2<'a> {
+ s: &'a str,
+ }
+ let _ = S2 { s: &*s };
+
+ struct S3<'a, T: ?Sized>(&'a T);
+ let _ = S3(&*s); // Don't lint. Inferred type would change.
+
+ struct S4<'a, T: ?Sized> {
+ s: &'a T,
+ }
+ let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
+
+ enum E1<'a> {
+ S1(&'a str),
+ S2 { s: &'a str },
+ }
+ impl<'a> E1<'a> {
+ fn m1(s: &'a String) {
+ let _ = Self::S1(&**s);
+ let _ = Self::S2 { s: &**s };
+ }
+ }
+ let _ = E1::S1(&*s);
+ let _ = E1::S2 { s: &*s };
+
+ enum E2<'a, T: ?Sized> {
+ S1(&'a T),
+ S2 { s: &'a T },
+ }
+ let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
+ let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
+
+ let ref_s = &s;
+ let _: &String = &*ref_s; // Don't lint reborrow.
+ f_string(&*ref_s); // Don't lint reborrow.
+
+ struct S5 {
+ foo: u32,
+ }
+ let b = Box::new(Box::new(S5 { foo: 5 }));
+ let _ = b.foo;
+ let _ = (*b).foo;
+ let _ = (**b).foo;
+
+ struct S6 {
+ foo: S5,
+ }
+ impl core::ops::Deref for S6 {
+ type Target = S5;
+ fn deref(&self) -> &Self::Target {
+ &self.foo
+ }
+ }
+ let s6 = S6 { foo: S5 { foo: 5 } };
+ let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
+
+ let ref_str = &"foo";
+ let _ = f_str(*ref_str);
+ let ref_ref_str = &ref_str;
+ let _ = f_str(**ref_ref_str);
+
+ fn _f5(x: &u32) -> u32 {
+ if true {
+ *x
+ } else {
+ return *x;
+ }
+ }
+
+ f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
+ f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
+
+ let x = &&40;
+ unsafe {
+ var(0, &**x);
+ }
+
+ let s = &"str";
+ let _ = || return *s;
+ let _ = || -> &'static str { return *s };
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
new file mode 100644
index 000000000..92765307e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
@@ -0,0 +1,202 @@
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:69:20
+ |
+LL | let _: &str = &*s;
+ | ^^ help: try this: `s`
+ |
+ = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:73:12
+ |
+LL | f_str(&*s);
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:77:14
+ |
+LL | f_str_t(&*s, &*s); // Don't lint second param.
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:80:25
+ |
+LL | let _: &Box<i32> = &**b;
+ | ^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:86:8
+ |
+LL | c(&*s);
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:92:9
+ |
+LL | &**x
+ | ^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:96:11
+ |
+LL | { &**x }
+ | ^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:100:9
+ |
+LL | &**{ x }
+ | ^^^^^^^^ help: try this: `{ x }`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:104:9
+ |
+LL | &***x
+ | ^^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:121:13
+ |
+LL | f1(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:122:13
+ |
+LL | f2(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:123:13
+ |
+LL | f3(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:124:28
+ |
+LL | f4.callable_str()(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:125:13
+ |
+LL | f5(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:126:13
+ |
+LL | f6(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:127:28
+ |
+LL | f7.callable_str()(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:128:26
+ |
+LL | f8.callable_t()(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:129:13
+ |
+LL | f9(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:130:14
+ |
+LL | f10(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:131:27
+ |
+LL | f11.callable_t()(&*x);
+ | ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:135:17
+ |
+LL | let _ = S1(&*s);
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:140:22
+ |
+LL | let _ = S2 { s: &*s };
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:156:30
+ |
+LL | let _ = Self::S1(&**s);
+ | ^^^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:157:35
+ |
+LL | let _ = Self::S2 { s: &**s };
+ | ^^^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:160:21
+ |
+LL | let _ = E1::S1(&*s);
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:161:26
+ |
+LL | let _ = E1::S2 { s: &*s };
+ | ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:179:13
+ |
+LL | let _ = (*b).foo;
+ | ^^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:180:13
+ |
+LL | let _ = (**b).foo;
+ | ^^^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:195:19
+ |
+LL | let _ = f_str(*ref_str);
+ | ^^^^^^^^ help: try this: `ref_str`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:197:19
+ |
+LL | let _ = f_str(**ref_ref_str);
+ | ^^^^^^^^^^^^^ help: try this: `ref_ref_str`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:207:13
+ |
+LL | f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
+ | ^^^^^^^^ help: try this: `ref_str`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:208:12
+ |
+LL | f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
+ | ^^^^^^^^^^ help: try this: `ref_str`
+
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:217:41
+ |
+LL | let _ = || -> &'static str { return *s };
+ | ^^ help: try this: `s`
+
+error: aborting due to 33 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
new file mode 100644
index 000000000..aa966761f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
@@ -0,0 +1,190 @@
+#![warn(clippy::explicit_counter_loop)]
+
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ _index = 0;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &mut vec {
+ _index += 1;
+ }
+
+ let mut _index = 0;
+ for _v in vec {
+ _index += 1;
+ }
+}
+
+mod issue_1219 {
+ pub fn test() {
+ // should not trigger the lint because variable is used after the loop #473
+ let vec = vec![1, 2, 3];
+ let mut index = 0;
+ for _v in &vec {
+ index += 1
+ }
+ println!("index: {}", index);
+
+ // should not trigger the lint because the count is conditional #1219
+ let text = "banana";
+ let mut count = 0;
+ for ch in text.chars() {
+ println!("{}", count);
+ if ch == 'a' {
+ continue;
+ }
+ count += 1;
+ }
+
+ // should not trigger the lint because the count is conditional
+ let text = "banana";
+ let mut count = 0;
+ for ch in text.chars() {
+ println!("{}", count);
+ if ch == 'a' {
+ count += 1;
+ }
+ }
+
+ // should trigger the lint because the count is not conditional
+ let text = "banana";
+ let mut count = 0;
+ for ch in text.chars() {
+ println!("{}", count);
+ count += 1;
+ if ch == 'a' {
+ continue;
+ }
+ }
+
+ // should trigger the lint because the count is not conditional
+ let text = "banana";
+ let mut count = 0;
+ for ch in text.chars() {
+ println!("{}", count);
+ count += 1;
+ for i in 0..2 {
+ let _ = 123;
+ }
+ }
+
+ // should not trigger the lint because the count is incremented multiple times
+ let text = "banana";
+ let mut count = 0;
+ for ch in text.chars() {
+ println!("{}", count);
+ count += 1;
+ for i in 0..2 {
+ count += 1;
+ }
+ }
+ }
+}
+
+mod issue_3308 {
+ pub fn test() {
+ // should not trigger the lint because the count is incremented multiple times
+ let mut skips = 0;
+ let erasures = vec![];
+ for i in 0..10 {
+ println!("{}", skips);
+ while erasures.contains(&(i + skips)) {
+ skips += 1;
+ }
+ }
+
+ // should not trigger the lint because the count is incremented multiple times
+ let mut skips = 0;
+ for i in 0..10 {
+ println!("{}", skips);
+ let mut j = 0;
+ while j < 5 {
+ skips += 1;
+ j += 1;
+ }
+ }
+
+ // should not trigger the lint because the count is incremented multiple times
+ let mut skips = 0;
+ for i in 0..10 {
+ println!("{}", skips);
+ for j in 0..5 {
+ skips += 1;
+ }
+ }
+ }
+}
+
+mod issue_1670 {
+ pub fn test() {
+ let mut count = 0;
+ for _i in 3..10 {
+ count += 1;
+ }
+ }
+}
+
+mod issue_4732 {
+ pub fn test() {
+ let slice = &[1, 2, 3];
+ let mut index = 0;
+
+ // should not trigger the lint because the count is used after the loop
+ for _v in slice {
+ index += 1
+ }
+ let _closure = || println!("index: {}", index);
+ }
+}
+
+mod issue_4677 {
+ pub fn test() {
+ let slice = &[1, 2, 3];
+
+ // should not trigger the lint because the count is used after incremented
+ let mut count = 0;
+ for _i in slice {
+ count += 1;
+ println!("{}", count);
+ }
+ }
+}
+
+mod issue_7920 {
+ pub fn test() {
+ let slice = &[1, 2, 3];
+
+ let index_usize: usize = 0;
+ let mut idx_usize: usize = 0;
+
+ // should suggest `enumerate`
+ for _item in slice {
+ if idx_usize == index_usize {
+ break;
+ }
+
+ idx_usize += 1;
+ }
+
+ let index_u32: u32 = 0;
+ let mut idx_u32: u32 = 0;
+
+ // should suggest `zip`
+ for _item in slice {
+ if idx_u32 == index_u32 {
+ break;
+ }
+
+ idx_u32 += 1;
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
new file mode 100644
index 000000000..f9f8407d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
@@ -0,0 +1,60 @@
+error: the variable `_index` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:6:5
+ |
+LL | for _v in &vec {
+ | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
+ |
+ = note: `-D clippy::explicit-counter-loop` implied by `-D warnings`
+
+error: the variable `_index` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:12:5
+ |
+LL | for _v in &vec {
+ | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
+
+error: the variable `_index` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:17:5
+ |
+LL | for _v in &mut vec {
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()`
+
+error: the variable `_index` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:22:5
+ |
+LL | for _v in vec {
+ | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()`
+
+error: the variable `count` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:61:9
+ |
+LL | for ch in text.chars() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
+
+error: the variable `count` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:72:9
+ |
+LL | for ch in text.chars() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
+
+error: the variable `count` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:130:9
+ |
+LL | for _i in 3..10 {
+ | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
+
+error: the variable `idx_usize` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:170:9
+ |
+LL | for _item in slice {
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()`
+
+error: the variable `idx_u32` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:182:9
+ |
+LL | for _item in slice {
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())`
+ |
+ = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
new file mode 100644
index 000000000..523cae183
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
@@ -0,0 +1,101 @@
+// run-rustfix
+
+#![allow(
+ unused_variables,
+ clippy::clone_double_ref,
+ clippy::needless_borrow,
+ clippy::borrow_deref_ref,
+ clippy::explicit_auto_deref
+)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut **a;
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", &*a, &*a);
+
+ println!("{}", &*a);
+
+ #[allow(clippy::match_single_binding)]
+ match &*a {
+ _ => (),
+ }
+
+ let b: String = concat(&*a);
+
+ let b = just_return(a);
+
+ let b: String = concat(just_return(a));
+
+ let b: &str = &**a;
+
+ let opt_a = Some(a.clone());
+ let b = &*opt_a.unwrap();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(&*a);
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
new file mode 100644
index 000000000..0bbc1ae57
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
@@ -0,0 +1,101 @@
+// run-rustfix
+
+#![allow(
+ unused_variables,
+ clippy::clone_double_ref,
+ clippy::needless_borrow,
+ clippy::borrow_deref_ref,
+ clippy::explicit_auto_deref
+)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = a.deref();
+
+ let b: &mut str = a.deref_mut();
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", a.deref(), a.deref());
+
+ println!("{}", a.deref());
+
+ #[allow(clippy::match_single_binding)]
+ match a.deref() {
+ _ => (),
+ }
+
+ let b: String = concat(a.deref());
+
+ let b = just_return(a).deref();
+
+ let b: String = concat(just_return(a).deref());
+
+ let b: &str = a.deref().deref();
+
+ let opt_a = Some(a.clone());
+ let b = opt_a.unwrap().deref();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(a.deref());
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
new file mode 100644
index 000000000..4b10ed137
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
@@ -0,0 +1,76 @@
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:36:19
+ |
+LL | let b: &str = a.deref();
+ | ^^^^^^^^^ help: try this: `&*a`
+ |
+ = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
+
+error: explicit `deref_mut` method call
+ --> $DIR/explicit_deref_methods.rs:38:23
+ |
+LL | let b: &mut str = a.deref_mut();
+ | ^^^^^^^^^^^^^ help: try this: `&mut **a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:41:39
+ |
+LL | let b: String = format!("{}, {}", a.deref(), a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:41:50
+ |
+LL | let b: String = format!("{}, {}", a.deref(), a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:43:20
+ |
+LL | println!("{}", a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:46:11
+ |
+LL | match a.deref() {
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:50:28
+ |
+LL | let b: String = concat(a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:52:13
+ |
+LL | let b = just_return(a).deref();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:54:28
+ |
+LL | let b: String = concat(just_return(a).deref());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:56:19
+ |
+LL | let b: &str = a.deref().deref();
+ | ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:59:13
+ |
+LL | let b = opt_a.unwrap().deref();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:85:31
+ |
+LL | let b: &str = expr_deref!(a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_write.fixed b/src/tools/clippy/tests/ui/explicit_write.fixed
new file mode 100644
index 000000000..74d0e5290
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_write.fixed
@@ -0,0 +1,63 @@
+// run-rustfix
+#![allow(unused_imports)]
+#![warn(clippy::explicit_write)]
+
+fn stdout() -> String {
+ String::new()
+}
+
+fn stderr() -> String {
+ String::new()
+}
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ // these should warn
+ {
+ use std::io::Write;
+ print!("test");
+ eprint!("test");
+ println!("test");
+ eprintln!("test");
+ print!("test");
+ eprint!("test");
+
+ // including newlines
+ println!("test\ntest");
+ eprintln!("test\ntest");
+
+ let value = 1;
+ eprintln!("with {}", value);
+ eprintln!("with {} {}", 2, value);
+ eprintln!("with {value}");
+ eprintln!("macro arg {}", one!());
+ }
+ // these should not warn, different destination
+ {
+ use std::fmt::Write;
+ let mut s = String::new();
+ write!(s, "test").unwrap();
+ write!(s, "test").unwrap();
+ writeln!(s, "test").unwrap();
+ writeln!(s, "test").unwrap();
+ s.write_fmt(format_args!("test")).unwrap();
+ s.write_fmt(format_args!("test")).unwrap();
+ write!(stdout(), "test").unwrap();
+ write!(stderr(), "test").unwrap();
+ writeln!(stdout(), "test").unwrap();
+ writeln!(stderr(), "test").unwrap();
+ stdout().write_fmt(format_args!("test")).unwrap();
+ stderr().write_fmt(format_args!("test")).unwrap();
+ }
+ // these should not warn, no unwrap
+ {
+ use std::io::Write;
+ std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
+ std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_write.rs b/src/tools/clippy/tests/ui/explicit_write.rs
new file mode 100644
index 000000000..e7a698d3e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_write.rs
@@ -0,0 +1,63 @@
+// run-rustfix
+#![allow(unused_imports)]
+#![warn(clippy::explicit_write)]
+
+fn stdout() -> String {
+ String::new()
+}
+
+fn stderr() -> String {
+ String::new()
+}
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ // these should warn
+ {
+ use std::io::Write;
+ write!(std::io::stdout(), "test").unwrap();
+ write!(std::io::stderr(), "test").unwrap();
+ writeln!(std::io::stdout(), "test").unwrap();
+ writeln!(std::io::stderr(), "test").unwrap();
+ std::io::stdout().write_fmt(format_args!("test")).unwrap();
+ std::io::stderr().write_fmt(format_args!("test")).unwrap();
+
+ // including newlines
+ writeln!(std::io::stdout(), "test\ntest").unwrap();
+ writeln!(std::io::stderr(), "test\ntest").unwrap();
+
+ let value = 1;
+ writeln!(std::io::stderr(), "with {}", value).unwrap();
+ writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
+ writeln!(std::io::stderr(), "with {value}").unwrap();
+ writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
+ }
+ // these should not warn, different destination
+ {
+ use std::fmt::Write;
+ let mut s = String::new();
+ write!(s, "test").unwrap();
+ write!(s, "test").unwrap();
+ writeln!(s, "test").unwrap();
+ writeln!(s, "test").unwrap();
+ s.write_fmt(format_args!("test")).unwrap();
+ s.write_fmt(format_args!("test")).unwrap();
+ write!(stdout(), "test").unwrap();
+ write!(stderr(), "test").unwrap();
+ writeln!(stdout(), "test").unwrap();
+ writeln!(stderr(), "test").unwrap();
+ stdout().write_fmt(format_args!("test")).unwrap();
+ stderr().write_fmt(format_args!("test")).unwrap();
+ }
+ // these should not warn, no unwrap
+ {
+ use std::io::Write;
+ std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
+ std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_write.stderr b/src/tools/clippy/tests/ui/explicit_write.stderr
new file mode 100644
index 000000000..29ae0cdec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_write.stderr
@@ -0,0 +1,76 @@
+error: use of `write!(stdout(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:23:9
+ |
+LL | write!(std::io::stdout(), "test").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
+ |
+ = note: `-D clippy::explicit-write` implied by `-D warnings`
+
+error: use of `write!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:24:9
+ |
+LL | write!(std::io::stderr(), "test").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
+
+error: use of `writeln!(stdout(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:25:9
+ |
+LL | writeln!(std::io::stdout(), "test").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:26:9
+ |
+LL | writeln!(std::io::stderr(), "test").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
+
+error: use of `stdout().write_fmt(...).unwrap()`
+ --> $DIR/explicit_write.rs:27:9
+ |
+LL | std::io::stdout().write_fmt(format_args!("test")).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
+
+error: use of `stderr().write_fmt(...).unwrap()`
+ --> $DIR/explicit_write.rs:28:9
+ |
+LL | std::io::stderr().write_fmt(format_args!("test")).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
+
+error: use of `writeln!(stdout(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:31:9
+ |
+LL | writeln!(std::io::stdout(), "test/ntest").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:32:9
+ |
+LL | writeln!(std::io::stderr(), "test/ntest").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:35:9
+ |
+LL | writeln!(std::io::stderr(), "with {}", value).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:36:9
+ |
+LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:37:9
+ |
+LL | writeln!(std::io::stderr(), "with {value}").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:38:9
+ |
+LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/extend_with_drain.fixed b/src/tools/clippy/tests/ui/extend_with_drain.fixed
new file mode 100644
index 000000000..71ebad24c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extend_with_drain.fixed
@@ -0,0 +1,60 @@
+// run-rustfix
+#![warn(clippy::extend_with_drain)]
+#![allow(clippy::iter_with_drain)]
+use std::collections::BinaryHeap;
+fn main() {
+ //gets linted
+ let mut vec1 = vec![0u8; 1024];
+ let mut vec2: std::vec::Vec<u8> = Vec::new();
+ vec2.append(&mut vec1);
+
+ let mut vec3 = vec![0u8; 1024];
+ let mut vec4: std::vec::Vec<u8> = Vec::new();
+
+ vec4.append(&mut vec3);
+
+ let mut vec11: std::vec::Vec<u8> = Vec::new();
+
+ vec11.append(&mut return_vector());
+
+ //won't get linted it doesn't move the entire content of a vec into another
+ let mut test1 = vec![0u8, 10];
+ let mut test2: std::vec::Vec<u8> = Vec::new();
+
+ test2.extend(test1.drain(4..10));
+
+ let mut vec3 = vec![0u8; 104];
+ let mut vec7: std::vec::Vec<u8> = Vec::new();
+
+ vec3.append(&mut vec7);
+
+ let mut vec5 = vec![0u8; 1024];
+ let mut vec6: std::vec::Vec<u8> = Vec::new();
+
+ vec5.extend(vec6.drain(..4));
+
+ let mut vec9: std::vec::Vec<u8> = Vec::new();
+
+ return_vector().append(&mut vec9);
+
+ //won't get linted because it is not a vec
+
+ let mut heap = BinaryHeap::from(vec![1, 3]);
+ let mut heap2 = BinaryHeap::from(vec![]);
+ heap2.extend(heap.drain());
+
+ let mut x = vec![0, 1, 2, 3, 5];
+ let ref_x = &mut x;
+ let mut y = Vec::new();
+ y.append(ref_x);
+}
+
+fn return_vector() -> Vec<u8> {
+ let mut new_vector = vec![];
+
+ for i in 1..10 {
+ new_vector.push(i)
+ }
+
+ new_vector
+}
diff --git a/src/tools/clippy/tests/ui/extend_with_drain.rs b/src/tools/clippy/tests/ui/extend_with_drain.rs
new file mode 100644
index 000000000..e9f011abb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extend_with_drain.rs
@@ -0,0 +1,60 @@
+// run-rustfix
+#![warn(clippy::extend_with_drain)]
+#![allow(clippy::iter_with_drain)]
+use std::collections::BinaryHeap;
+fn main() {
+ //gets linted
+ let mut vec1 = vec![0u8; 1024];
+ let mut vec2: std::vec::Vec<u8> = Vec::new();
+ vec2.extend(vec1.drain(..));
+
+ let mut vec3 = vec![0u8; 1024];
+ let mut vec4: std::vec::Vec<u8> = Vec::new();
+
+ vec4.extend(vec3.drain(..));
+
+ let mut vec11: std::vec::Vec<u8> = Vec::new();
+
+ vec11.extend(return_vector().drain(..));
+
+ //won't get linted it doesn't move the entire content of a vec into another
+ let mut test1 = vec![0u8, 10];
+ let mut test2: std::vec::Vec<u8> = Vec::new();
+
+ test2.extend(test1.drain(4..10));
+
+ let mut vec3 = vec![0u8; 104];
+ let mut vec7: std::vec::Vec<u8> = Vec::new();
+
+ vec3.append(&mut vec7);
+
+ let mut vec5 = vec![0u8; 1024];
+ let mut vec6: std::vec::Vec<u8> = Vec::new();
+
+ vec5.extend(vec6.drain(..4));
+
+ let mut vec9: std::vec::Vec<u8> = Vec::new();
+
+ return_vector().append(&mut vec9);
+
+ //won't get linted because it is not a vec
+
+ let mut heap = BinaryHeap::from(vec![1, 3]);
+ let mut heap2 = BinaryHeap::from(vec![]);
+ heap2.extend(heap.drain());
+
+ let mut x = vec![0, 1, 2, 3, 5];
+ let ref_x = &mut x;
+ let mut y = Vec::new();
+ y.extend(ref_x.drain(..));
+}
+
+fn return_vector() -> Vec<u8> {
+ let mut new_vector = vec![];
+
+ for i in 1..10 {
+ new_vector.push(i)
+ }
+
+ new_vector
+}
diff --git a/src/tools/clippy/tests/ui/extend_with_drain.stderr b/src/tools/clippy/tests/ui/extend_with_drain.stderr
new file mode 100644
index 000000000..da14ddb25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extend_with_drain.stderr
@@ -0,0 +1,28 @@
+error: use of `extend` instead of `append` for adding the full range of a second vector
+ --> $DIR/extend_with_drain.rs:9:5
+ |
+LL | vec2.extend(vec1.drain(..));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)`
+ |
+ = note: `-D clippy::extend-with-drain` implied by `-D warnings`
+
+error: use of `extend` instead of `append` for adding the full range of a second vector
+ --> $DIR/extend_with_drain.rs:14:5
+ |
+LL | vec4.extend(vec3.drain(..));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)`
+
+error: use of `extend` instead of `append` for adding the full range of a second vector
+ --> $DIR/extend_with_drain.rs:18:5
+ |
+LL | vec11.extend(return_vector().drain(..));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())`
+
+error: use of `extend` instead of `append` for adding the full range of a second vector
+ --> $DIR/extend_with_drain.rs:49:5
+ |
+LL | y.extend(ref_x.drain(..));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `y.append(ref_x)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
new file mode 100644
index 000000000..d6631e012
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
@@ -0,0 +1,129 @@
+// aux-build:proc_macro_derive.rs
+
+#![allow(
+ unused,
+ dead_code,
+ clippy::needless_lifetimes,
+ clippy::needless_pass_by_value,
+ clippy::needless_arbitrary_self_type
+)]
+#![warn(clippy::extra_unused_lifetimes)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+fn empty() {}
+
+fn used_lt<'a>(x: &'a u8) {}
+
+fn unused_lt<'a>(x: u8) {}
+
+fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
+ // 'a is useless here since it's not directly bound
+}
+
+fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 {
+ panic!()
+}
+
+fn lt_return_only<'a>() -> &'a u8 {
+ panic!()
+}
+
+fn unused_lt_blergh<'a>(x: Option<Box<dyn Send + 'a>>) {}
+
+trait Foo<'a> {
+ fn x(&self, a: &'a u8);
+}
+
+impl<'a> Foo<'a> for u8 {
+ fn x(&self, a: &'a u8) {}
+}
+
+struct Bar;
+
+impl Bar {
+ fn x<'a>(&self) {}
+}
+
+// test for #489 (used lifetimes in bounds)
+pub fn parse<'a, I: Iterator<Item = &'a str>>(_it: &mut I) {
+ unimplemented!()
+}
+pub fn parse2<'a, I>(_it: &mut I)
+where
+ I: Iterator<Item = &'a str>,
+{
+ unimplemented!()
+}
+
+struct X {
+ x: u32,
+}
+
+impl X {
+ fn self_ref_with_lifetime<'a>(&'a self) {}
+ fn explicit_self_with_lifetime<'a>(self: &'a Self) {}
+}
+
+// Methods implementing traits must have matching lifetimes
+mod issue4291 {
+ trait BadTrait {
+ fn unused_lt<'a>(x: u8) {}
+ }
+
+ impl BadTrait for () {
+ fn unused_lt<'a>(_x: u8) {}
+ }
+}
+
+mod issue6437 {
+ pub struct Scalar;
+
+ impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+ fn add_assign(&mut self, _rhs: &Scalar) {
+ unimplemented!();
+ }
+ }
+
+ impl<'b> Scalar {
+ pub fn something<'c>() -> Self {
+ Self
+ }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod first_case {
+ use serde::de::Visitor;
+ pub trait Expected {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter);
+ }
+
+ impl<'de, T> Expected for T
+ where
+ T: Visitor<'de>,
+ {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter) {}
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod second_case {
+ pub trait Source {
+ fn hey();
+ }
+
+ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
+ fn hey() {}
+ }
+}
+
+// Should not lint
+#[derive(ExtraLifetimeDerive)]
+struct Human<'a> {
+ pub bones: i32,
+ pub name: &'a str,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
new file mode 100644
index 000000000..26ebc3976
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
@@ -0,0 +1,40 @@
+error: this lifetime isn't used in the function definition
+ --> $DIR/extra_unused_lifetimes.rs:19:14
+ |
+LL | fn unused_lt<'a>(x: u8) {}
+ | ^^
+ |
+ = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
+
+error: this lifetime isn't used in the function definition
+ --> $DIR/extra_unused_lifetimes.rs:46:10
+ |
+LL | fn x<'a>(&self) {}
+ | ^^
+
+error: this lifetime isn't used in the function definition
+ --> $DIR/extra_unused_lifetimes.rs:72:22
+ |
+LL | fn unused_lt<'a>(x: u8) {}
+ | ^^
+
+error: this lifetime isn't used in the impl
+ --> $DIR/extra_unused_lifetimes.rs:83:10
+ |
+LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+ | ^^
+
+error: this lifetime isn't used in the impl
+ --> $DIR/extra_unused_lifetimes.rs:89:10
+ |
+LL | impl<'b> Scalar {
+ | ^^
+
+error: this lifetime isn't used in the function definition
+ --> $DIR/extra_unused_lifetimes.rs:90:26
+ |
+LL | pub fn something<'c>() -> Self {
+ | ^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.rs b/src/tools/clippy/tests/ui/fallible_impl_from.rs
new file mode 100644
index 000000000..5d5af4e46
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fallible_impl_from.rs
@@ -0,0 +1,76 @@
+#![deny(clippy::fallible_impl_from)]
+
+// docs example
+struct Foo(i32);
+impl From<String> for Foo {
+ fn from(s: String) -> Self {
+ Foo(s.parse().unwrap())
+ }
+}
+
+struct Valid(Vec<u8>);
+
+impl<'a> From<&'a str> for Valid {
+ fn from(s: &'a str) -> Valid {
+ Valid(s.to_owned().into_bytes())
+ }
+}
+impl From<usize> for Valid {
+ fn from(i: usize) -> Valid {
+ Valid(Vec::with_capacity(i))
+ }
+}
+
+struct Invalid;
+
+impl From<usize> for Invalid {
+ fn from(i: usize) -> Invalid {
+ if i != 42 {
+ panic!();
+ }
+ Invalid
+ }
+}
+
+impl From<Option<String>> for Invalid {
+ fn from(s: Option<String>) -> Invalid {
+ let s = s.unwrap();
+ if !s.is_empty() {
+ panic!("42");
+ } else if s.parse::<u32>().unwrap() != 42 {
+ panic!("{:?}", s);
+ }
+ Invalid
+ }
+}
+
+trait ProjStrTrait {
+ type ProjString;
+}
+impl<T> ProjStrTrait for Box<T> {
+ type ProjString = String;
+}
+impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
+ fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
+ if s.parse::<u32>().ok().unwrap() != 42 {
+ panic!("{:?}", s);
+ }
+ Invalid
+ }
+}
+
+struct Unreachable;
+
+impl From<String> for Unreachable {
+ fn from(s: String) -> Unreachable {
+ if s.is_empty() {
+ return Unreachable;
+ }
+ match s.chars().next() {
+ Some(_) => Unreachable,
+ None => unreachable!(), // do not lint the unreachable macro
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.stderr b/src/tools/clippy/tests/ui/fallible_impl_from.stderr
new file mode 100644
index 000000000..d637dbce5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fallible_impl_from.stderr
@@ -0,0 +1,93 @@
+error: consider implementing `TryFrom` instead
+ --> $DIR/fallible_impl_from.rs:5:1
+ |
+LL | / impl From<String> for Foo {
+LL | | fn from(s: String) -> Self {
+LL | | Foo(s.parse().unwrap())
+LL | | }
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/fallible_impl_from.rs:1:9
+ |
+LL | #![deny(clippy::fallible_impl_from)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
+ --> $DIR/fallible_impl_from.rs:7:13
+ |
+LL | Foo(s.parse().unwrap())
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider implementing `TryFrom` instead
+ --> $DIR/fallible_impl_from.rs:26:1
+ |
+LL | / impl From<usize> for Invalid {
+LL | | fn from(i: usize) -> Invalid {
+LL | | if i != 42 {
+LL | | panic!();
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
+ --> $DIR/fallible_impl_from.rs:29:13
+ |
+LL | panic!();
+ | ^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
+ --> $DIR/fallible_impl_from.rs:35:1
+ |
+LL | / impl From<Option<String>> for Invalid {
+LL | | fn from(s: Option<String>) -> Invalid {
+LL | | let s = s.unwrap();
+LL | | if !s.is_empty() {
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
+ --> $DIR/fallible_impl_from.rs:37:17
+ |
+LL | let s = s.unwrap();
+ | ^^^^^^^^^^
+LL | if !s.is_empty() {
+LL | panic!("42");
+ | ^^^^^^^^^^^^
+LL | } else if s.parse::<u32>().unwrap() != 42 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | panic!("{:?}", s);
+ | ^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: consider implementing `TryFrom` instead
+ --> $DIR/fallible_impl_from.rs:53:1
+ |
+LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
+LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
+LL | | if s.parse::<u32>().ok().unwrap() != 42 {
+LL | | panic!("{:?}", s);
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
+note: potential failure(s)
+ --> $DIR/fallible_impl_from.rs:55:12
+ |
+LL | if s.parse::<u32>().ok().unwrap() != 42 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | panic!("{:?}", s);
+ | ^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
new file mode 100644
index 000000000..7367910ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
@@ -0,0 +1,249 @@
+// aux-build:proc_macro_derive.rs
+// aux-build:macro_rules.rs
+
+#![warn(clippy::field_reassign_with_default)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+#[macro_use]
+extern crate macro_rules;
+
+// Don't lint on derives that derive `Default`
+// See https://github.com/rust-lang/rust-clippy/issues/6545
+#[derive(FieldReassignWithDefault)]
+struct DerivedStruct;
+
+#[derive(Default)]
+struct A {
+ i: i32,
+ j: i64,
+}
+
+struct B {
+ i: i32,
+ j: i64,
+}
+
+#[derive(Default)]
+struct C {
+ i: Vec<i32>,
+ j: i64,
+}
+
+#[derive(Default)]
+struct D {
+ a: Option<i32>,
+ b: Option<i32>,
+}
+
+macro_rules! m {
+ ($key:ident: $value:tt) => {{
+ let mut data = $crate::D::default();
+ data.$key = Some($value);
+ data
+ }};
+}
+
+/// Implements .next() that returns a different number each time.
+struct SideEffect(i32);
+
+impl SideEffect {
+ fn new() -> SideEffect {
+ SideEffect(0)
+ }
+ fn next(&mut self) -> i32 {
+ self.0 += 1;
+ self.0
+ }
+}
+
+fn main() {
+ // wrong, produces first error in stderr
+ let mut a: A = Default::default();
+ a.i = 42;
+
+ // right
+ let mut a: A = Default::default();
+
+ // right
+ let a = A {
+ i: 42,
+ ..Default::default()
+ };
+
+ // right
+ let mut a: A = Default::default();
+ if a.i == 0 {
+ a.j = 12;
+ }
+
+ // right
+ let mut a: A = Default::default();
+ let b = 5;
+
+ // right
+ let mut b = 32;
+ let mut a: A = Default::default();
+ b = 2;
+
+ // right
+ let b: B = B { i: 42, j: 24 };
+
+ // right
+ let mut b: B = B { i: 42, j: 24 };
+ b.i = 52;
+
+ // right
+ let mut b = B { i: 15, j: 16 };
+ let mut a: A = Default::default();
+ b.i = 2;
+
+ // wrong, produces second error in stderr
+ let mut a: A = Default::default();
+ a.j = 43;
+ a.i = 42;
+
+ // wrong, produces third error in stderr
+ let mut a: A = Default::default();
+ a.i = 42;
+ a.j = 43;
+ a.j = 44;
+
+ // wrong, produces fourth error in stderr
+ let mut a = A::default();
+ a.i = 42;
+
+ // wrong, but does not produce an error in stderr, because we can't produce a correct kind of
+ // suggestion with current implementation
+ let mut c: (i32, i32) = Default::default();
+ c.0 = 42;
+ c.1 = 21;
+
+ // wrong, produces the fifth error in stderr
+ let mut a: A = Default::default();
+ a.i = Default::default();
+
+ // wrong, produces the sixth error in stderr
+ let mut a: A = Default::default();
+ a.i = Default::default();
+ a.j = 45;
+
+ // right, because an assignment refers to another field
+ let mut x = A::default();
+ x.i = 42;
+ x.j = 21 + x.i as i64;
+
+ // right, we bail out if there's a reassignment to the same variable, since there is a risk of
+ // side-effects affecting the outcome
+ let mut x = A::default();
+ let mut side_effect = SideEffect::new();
+ x.i = side_effect.next();
+ x.j = 2;
+ x.i = side_effect.next();
+
+ // don't lint - some private fields
+ let mut x = m::F::default();
+ x.a = 1;
+
+ // don't expand macros in the suggestion (#6522)
+ let mut a: C = C::default();
+ a.i = vec![1];
+
+ // Don't lint in external macros
+ field_reassign_with_default!();
+
+ // be sure suggestion is correct with generics
+ let mut a: Wrapper<bool> = Default::default();
+ a.i = true;
+
+ let mut a: WrapperMulti<i32, i64> = Default::default();
+ a.i = 42;
+
+ // Don't lint in macros
+ m! {
+ a: 42
+ };
+}
+
+mod m {
+ #[derive(Default)]
+ pub struct F {
+ pub a: u64,
+ b: u64,
+ }
+}
+
+#[derive(Default)]
+struct Wrapper<T> {
+ i: T,
+}
+
+#[derive(Default)]
+struct WrapperMulti<T, U> {
+ i: T,
+ j: U,
+}
+
+mod issue6312 {
+ use std::sync::atomic::AtomicBool;
+ use std::sync::Arc;
+
+ // do not lint: type implements `Drop` but not all fields are `Copy`
+ #[derive(Clone, Default)]
+ pub struct ImplDropNotAllCopy {
+ name: String,
+ delay_data_sync: Arc<AtomicBool>,
+ }
+
+ impl Drop for ImplDropNotAllCopy {
+ fn drop(&mut self) {
+ self.close()
+ }
+ }
+
+ impl ImplDropNotAllCopy {
+ fn new(name: &str) -> Self {
+ let mut f = ImplDropNotAllCopy::default();
+ f.name = name.to_owned();
+ f
+ }
+ fn close(&self) {}
+ }
+
+ // lint: type implements `Drop` and all fields are `Copy`
+ #[derive(Clone, Default)]
+ pub struct ImplDropAllCopy {
+ name: usize,
+ delay_data_sync: bool,
+ }
+
+ impl Drop for ImplDropAllCopy {
+ fn drop(&mut self) {
+ self.close()
+ }
+ }
+
+ impl ImplDropAllCopy {
+ fn new(name: &str) -> Self {
+ let mut f = ImplDropAllCopy::default();
+ f.name = name.len();
+ f
+ }
+ fn close(&self) {}
+ }
+
+ // lint: type does not implement `Drop` though all fields are `Copy`
+ #[derive(Clone, Default)]
+ pub struct NoDropAllCopy {
+ name: usize,
+ delay_data_sync: bool,
+ }
+
+ impl NoDropAllCopy {
+ fn new(name: &str) -> Self {
+ let mut f = NoDropAllCopy::default();
+ f.name = name.len();
+ f
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
new file mode 100644
index 000000000..3ce4b91a5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
@@ -0,0 +1,135 @@
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:63:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
+note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:62:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:103:5
+ |
+LL | a.j = 43;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:102:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:108:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:107:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:114:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:113:5
+ |
+LL | let mut a = A::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:124:5
+ |
+LL | a.i = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:123:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:128:5
+ |
+LL | a.i = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:127:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:150:5
+ |
+LL | a.i = vec![1];
+ | ^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:149:5
+ |
+LL | let mut a: C = C::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:157:5
+ |
+LL | a.i = true;
+ | ^^^^^^^^^^^
+ |
+note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:156:5
+ |
+LL | let mut a: Wrapper<bool> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:160:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:159:5
+ |
+LL | let mut a: WrapperMulti<i32, i64> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:229:13
+ |
+LL | f.name = name.len();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:228:13
+ |
+LL | let mut f = ImplDropAllCopy::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:245:13
+ |
+LL | f.name = name.len();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:244:13
+ |
+LL | let mut f = NoDropAllCopy::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/filetype_is_file.rs b/src/tools/clippy/tests/ui/filetype_is_file.rs
new file mode 100644
index 000000000..5de8fe8cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filetype_is_file.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::filetype_is_file)]
+
+fn main() -> std::io::Result<()> {
+ use std::fs;
+ use std::ops::BitOr;
+
+ // !filetype.is_dir()
+ if fs::metadata("foo.txt")?.file_type().is_file() {
+ // read file
+ }
+
+ // positive of filetype.is_dir()
+ if !fs::metadata("foo.txt")?.file_type().is_file() {
+ // handle dir
+ }
+
+ // false positive of filetype.is_dir()
+ if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
+ // ...
+ }
+
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/filetype_is_file.stderr b/src/tools/clippy/tests/ui/filetype_is_file.stderr
new file mode 100644
index 000000000..cd1e3ac37
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filetype_is_file.stderr
@@ -0,0 +1,27 @@
+error: `FileType::is_file()` only covers regular files
+ --> $DIR/filetype_is_file.rs:8:8
+ |
+LL | if fs::metadata("foo.txt")?.file_type().is_file() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::filetype-is-file` implied by `-D warnings`
+ = help: use `!FileType::is_dir()` instead
+
+error: `!FileType::is_file()` only denies regular files
+ --> $DIR/filetype_is_file.rs:13:8
+ |
+LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `FileType::is_dir()` instead
+
+error: `FileType::is_file()` only covers regular files
+ --> $DIR/filetype_is_file.rs:18:9
+ |
+LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `!FileType::is_dir()` instead
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/filter_map_identity.fixed b/src/tools/clippy/tests/ui/filter_map_identity.fixed
new file mode 100644
index 000000000..a5860aa49
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_identity.fixed
@@ -0,0 +1,19 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::needless_return)]
+#![warn(clippy::filter_map_identity)]
+
+fn main() {
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.flatten();
+
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.flatten();
+
+ use std::convert::identity;
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.flatten();
+
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.flatten();
+}
diff --git a/src/tools/clippy/tests/ui/filter_map_identity.rs b/src/tools/clippy/tests/ui/filter_map_identity.rs
new file mode 100644
index 000000000..7e998b9cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_identity.rs
@@ -0,0 +1,19 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::needless_return)]
+#![warn(clippy::filter_map_identity)]
+
+fn main() {
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.filter_map(|x| x);
+
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.filter_map(std::convert::identity);
+
+ use std::convert::identity;
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.filter_map(identity);
+
+ let iterator = vec![Some(1), None, Some(2)].into_iter();
+ let _ = iterator.filter_map(|x| return x);
+}
diff --git a/src/tools/clippy/tests/ui/filter_map_identity.stderr b/src/tools/clippy/tests/ui/filter_map_identity.stderr
new file mode 100644
index 000000000..43c9fdca4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_identity.stderr
@@ -0,0 +1,28 @@
+error: use of `filter_map` with an identity function
+ --> $DIR/filter_map_identity.rs:8:22
+ |
+LL | let _ = iterator.filter_map(|x| x);
+ | ^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+ |
+ = note: `-D clippy::filter-map-identity` implied by `-D warnings`
+
+error: use of `filter_map` with an identity function
+ --> $DIR/filter_map_identity.rs:11:22
+ |
+LL | let _ = iterator.filter_map(std::convert::identity);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: use of `filter_map` with an identity function
+ --> $DIR/filter_map_identity.rs:15:22
+ |
+LL | let _ = iterator.filter_map(identity);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: use of `filter_map` with an identity function
+ --> $DIR/filter_map_identity.rs:18:22
+ |
+LL | let _ = iterator.filter_map(|x| return x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs
new file mode 100644
index 000000000..dbeb23543
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_next.rs
@@ -0,0 +1,17 @@
+#![warn(clippy::all, clippy::pedantic)]
+
+fn main() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ #[rustfmt::skip]
+ let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
+ .into_iter()
+ .filter_map(|x| {
+ if x == 2 {
+ Some(x * 2)
+ } else {
+ None
+ }
+ })
+ .next();
+}
diff --git a/src/tools/clippy/tests/ui/filter_map_next.stderr b/src/tools/clippy/tests/ui/filter_map_next.stderr
new file mode 100644
index 000000000..ddc982c93
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_next.stderr
@@ -0,0 +1,17 @@
+error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
+ --> $DIR/filter_map_next.rs:7:26
+ |
+LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
+ | __________________________^
+LL | | .into_iter()
+LL | | .filter_map(|x| {
+LL | | if x == 2 {
+... |
+LL | | })
+LL | | .next();
+ | |_______________^
+ |
+ = note: `-D clippy::filter-map-next` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
new file mode 100644
index 000000000..c3992d7e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+
+fn main() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
+ assert_eq!(element, Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs
new file mode 100644
index 000000000..447219a96
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+
+fn main() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
+ assert_eq!(element, Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
new file mode 100644
index 000000000..3bb062ffd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
@@ -0,0 +1,10 @@
+error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
+ --> $DIR/filter_map_next_fixable.rs:8:32
+ |
+LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
+ |
+ = note: `-D clippy::filter-map-next` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs
new file mode 100644
index 000000000..88d3b0e74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/find_map.rs
@@ -0,0 +1,33 @@
+#![warn(clippy::all, clippy::pedantic)]
+
+#[derive(Debug, Copy, Clone)]
+enum Flavor {
+ Chocolate,
+}
+
+#[derive(Debug, Copy, Clone)]
+enum Dessert {
+ Banana,
+ Pudding,
+ Cake(Flavor),
+}
+
+fn main() {
+ let desserts_of_the_week = vec![Dessert::Banana, Dessert::Cake(Flavor::Chocolate), Dessert::Pudding];
+
+ let a = ["lol", "NaN", "2", "5", "Xunda"];
+
+ let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
+
+ #[allow(clippy::match_like_matches_macro)]
+ let _: Option<Flavor> = desserts_of_the_week
+ .iter()
+ .find(|dessert| match *dessert {
+ Dessert::Cake(_) => true,
+ _ => false,
+ })
+ .map(|dessert| match *dessert {
+ Dessert::Cake(ref flavor) => *flavor,
+ _ => unreachable!(),
+ });
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_identity.fixed b/src/tools/clippy/tests/ui/flat_map_identity.fixed
new file mode 100644
index 000000000..1f4b880ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_identity.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::needless_return)]
+#![warn(clippy::flat_map_identity)]
+
+use std::convert;
+
+fn main() {
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flatten();
+
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flatten();
+
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flatten();
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_identity.rs b/src/tools/clippy/tests/ui/flat_map_identity.rs
new file mode 100644
index 000000000..de14a06d4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_identity.rs
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::needless_return)]
+#![warn(clippy::flat_map_identity)]
+
+use std::convert;
+
+fn main() {
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flat_map(|x| x);
+
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flat_map(convert::identity);
+
+ let iterator = [[0, 1], [2, 3], [4, 5]].iter();
+ let _ = iterator.flat_map(|x| return x);
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_identity.stderr b/src/tools/clippy/tests/ui/flat_map_identity.stderr
new file mode 100644
index 000000000..e776c9fdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_identity.stderr
@@ -0,0 +1,22 @@
+error: use of `flat_map` with an identity function
+ --> $DIR/flat_map_identity.rs:10:22
+ |
+LL | let _ = iterator.flat_map(|x| x);
+ | ^^^^^^^^^^^^^^^ help: try: `flatten()`
+ |
+ = note: `-D clippy::flat-map-identity` implied by `-D warnings`
+
+error: use of `flat_map` with an identity function
+ --> $DIR/flat_map_identity.rs:13:22
+ |
+LL | let _ = iterator.flat_map(convert::identity);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: use of `flat_map` with an identity function
+ --> $DIR/flat_map_identity.rs:16:22
+ |
+LL | let _ = iterator.flat_map(|x| return x);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/flat_map_option.fixed b/src/tools/clippy/tests/ui/flat_map_option.fixed
new file mode 100644
index 000000000..6a34f0089
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+ // yay
+ let c = |x| Some(x);
+ let _ = [1].iter().filter_map(c);
+ let _ = [1].iter().filter_map(Some);
+
+ // nay
+ let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.rs b/src/tools/clippy/tests/ui/flat_map_option.rs
new file mode 100644
index 000000000..2479abddb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+ // yay
+ let c = |x| Some(x);
+ let _ = [1].iter().flat_map(c);
+ let _ = [1].iter().flat_map(Some);
+
+ // nay
+ let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.stderr b/src/tools/clippy/tests/ui/flat_map_option.stderr
new file mode 100644
index 000000000..a9d8056de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.stderr
@@ -0,0 +1,16 @@
+error: used `flat_map` where `filter_map` could be used instead
+ --> $DIR/flat_map_option.rs:8:24
+ |
+LL | let _ = [1].iter().flat_map(c);
+ | ^^^^^^^^ help: try: `filter_map`
+ |
+ = note: `-D clippy::flat-map-option` implied by `-D warnings`
+
+error: used `flat_map` where `filter_map` could be used instead
+ --> $DIR/flat_map_option.rs:9:24
+ |
+LL | let _ = [1].iter().flat_map(Some);
+ | ^^^^^^^^ help: try: `filter_map`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/float_arithmetic.rs b/src/tools/clippy/tests/ui/float_arithmetic.rs
new file mode 100644
index 000000000..60fa7569e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_arithmetic.rs
@@ -0,0 +1,52 @@
+#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
+#![allow(
+ unused,
+ clippy::shadow_reuse,
+ clippy::shadow_unrelated,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::op_ref
+)]
+
+#[rustfmt::skip]
+fn main() {
+ let mut f = 1.0f32;
+
+ f * 2.0;
+
+ 1.0 + f;
+ f * 2.0;
+ f / 2.0;
+ f - 2.0 * 4.2;
+ -f;
+
+ f += 1.0;
+ f -= 1.0;
+ f *= 2.0;
+ f /= 2.0;
+}
+
+// also warn about floating point arith with references involved
+
+pub fn float_arith_ref() {
+ 3.1_f32 + &1.2_f32;
+ &3.4_f32 + 1.5_f32;
+ &3.5_f32 + &1.3_f32;
+}
+
+pub fn float_foo(f: &f32) -> f32 {
+ let a = 5.1;
+ a + f
+}
+
+pub fn float_bar(f1: &f32, f2: &f32) -> f32 {
+ f1 + f2
+}
+
+pub fn float_baz(f1: f32, f2: &f32) -> f32 {
+ f1 + f2
+}
+
+pub fn float_qux(f1: f32, f2: f32) -> f32 {
+ (&f1 + &f2)
+}
diff --git a/src/tools/clippy/tests/ui/float_arithmetic.stderr b/src/tools/clippy/tests/ui/float_arithmetic.stderr
new file mode 100644
index 000000000..1ceffb35b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_arithmetic.stderr
@@ -0,0 +1,106 @@
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:15:5
+ |
+LL | f * 2.0;
+ | ^^^^^^^
+ |
+ = note: `-D clippy::float-arithmetic` implied by `-D warnings`
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:17:5
+ |
+LL | 1.0 + f;
+ | ^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:18:5
+ |
+LL | f * 2.0;
+ | ^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:19:5
+ |
+LL | f / 2.0;
+ | ^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:20:5
+ |
+LL | f - 2.0 * 4.2;
+ | ^^^^^^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:21:5
+ |
+LL | -f;
+ | ^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:23:5
+ |
+LL | f += 1.0;
+ | ^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:24:5
+ |
+LL | f -= 1.0;
+ | ^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:25:5
+ |
+LL | f *= 2.0;
+ | ^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:26:5
+ |
+LL | f /= 2.0;
+ | ^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:32:5
+ |
+LL | 3.1_f32 + &1.2_f32;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:33:5
+ |
+LL | &3.4_f32 + 1.5_f32;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:34:5
+ |
+LL | &3.5_f32 + &1.3_f32;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:39:5
+ |
+LL | a + f
+ | ^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:43:5
+ |
+LL | f1 + f2
+ | ^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:47:5
+ |
+LL | f1 + f2
+ | ^^^^^^^
+
+error: floating-point arithmetic detected
+ --> $DIR/float_arithmetic.rs:51:5
+ |
+LL | (&f1 + &f2)
+ | ^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs
new file mode 100644
index 000000000..a34458b94
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_cmp.rs
@@ -0,0 +1,115 @@
+#![warn(clippy::float_cmp)]
+#![allow(
+ unused,
+ clippy::no_effect,
+ clippy::op_ref,
+ clippy::unnecessary_operation,
+ clippy::cast_lossless
+)]
+
+use std::ops::Add;
+
+const ZERO: f32 = 0.0;
+const ONE: f32 = ZERO + 1.0;
+
+fn twice<T>(x: T) -> T
+where
+ T: Add<T, Output = T> + Copy,
+{
+ x + x
+}
+
+fn eq_fl(x: f32, y: f32) -> bool {
+ if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn
+}
+
+fn fl_eq(x: f32, y: f32) -> bool {
+ if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn
+}
+
+struct X {
+ val: f32,
+}
+
+impl PartialEq for X {
+ fn eq(&self, o: &X) -> bool {
+ if self.val.is_nan() {
+ o.val.is_nan()
+ } else {
+ self.val == o.val // no error, inside "eq" fn
+ }
+ }
+}
+
+fn main() {
+ ZERO == 0f32; //no error, comparison with zero is ok
+ 1.0f32 != f32::INFINITY; // also comparison with infinity
+ 1.0f32 != f32::NEG_INFINITY; // and negative infinity
+ ZERO == 0.0; //no error, comparison with zero is ok
+ ZERO + ZERO != 1.0; //no error, comparison with zero is ok
+
+ ONE == 1f32;
+ ONE == 1.0 + 0.0;
+ ONE + ONE == ZERO + ONE + ONE;
+ ONE != 2.0;
+ ONE != 0.0; // no error, comparison with zero is ok
+ twice(ONE) != ONE;
+ ONE as f64 != 2.0;
+ ONE as f64 != 0.0; // no error, comparison with zero is ok
+
+ let x: f64 = 1.0;
+
+ x == 1.0;
+ x != 0f64; // no error, comparison with zero is ok
+
+ twice(x) != twice(ONE as f64);
+
+ x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
+ x > 0.0;
+ x <= 0.0;
+ x >= 0.0;
+
+ let xs: [f32; 1] = [0.0];
+ let a: *const f32 = xs.as_ptr();
+ let b: *const f32 = xs.as_ptr();
+
+ assert_eq!(a, b); // no errors
+
+ const ZERO_ARRAY: [f32; 2] = [0.0, 0.0];
+ const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1];
+
+ let i = 0;
+ let j = 1;
+
+ ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i
+ NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
+
+ let a1: [f32; 1] = [0.0];
+ let a2: [f32; 1] = [1.1];
+
+ a1 == a2;
+ a1[0] == a2[0];
+
+ // no errors - comparing signums is ok
+ let x32 = 3.21f32;
+ 1.23f32.signum() == x32.signum();
+ 1.23f32.signum() == -(x32.signum());
+ 1.23f32.signum() == 3.21f32.signum();
+
+ 1.23f32.signum() != x32.signum();
+ 1.23f32.signum() != -(x32.signum());
+ 1.23f32.signum() != 3.21f32.signum();
+
+ let x64 = 3.21f64;
+ 1.23f64.signum() == x64.signum();
+ 1.23f64.signum() == -(x64.signum());
+ 1.23f64.signum() == 3.21f64.signum();
+
+ 1.23f64.signum() != x64.signum();
+ 1.23f64.signum() != -(x64.signum());
+ 1.23f64.signum() != 3.21f64.signum();
+
+ // the comparison should also look through references
+ &0.0 == &ZERO;
+ &&&&0.0 == &&&&ZERO;
+}
diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr
new file mode 100644
index 000000000..9cc1f1b75
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_cmp.stderr
@@ -0,0 +1,51 @@
+error: strict comparison of `f32` or `f64`
+ --> $DIR/float_cmp.rs:57:5
+ |
+LL | ONE as f64 != 2.0;
+ | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
+ |
+ = note: `-D clippy::float-cmp` implied by `-D warnings`
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
+ --> $DIR/float_cmp.rs:62:5
+ |
+LL | x == 1.0;
+ | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
+ --> $DIR/float_cmp.rs:65:5
+ |
+LL | twice(x) != twice(ONE as f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
+ --> $DIR/float_cmp.rs:85:5
+ |
+LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` arrays
+ --> $DIR/float_cmp.rs:90:5
+ |
+LL | a1 == a2;
+ | ^^^^^^^^
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64`
+ --> $DIR/float_cmp.rs:91:5
+ |
+LL | a1[0] == a2[0];
+ | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/float_cmp_const.rs b/src/tools/clippy/tests/ui/float_cmp_const.rs
new file mode 100644
index 000000000..86ce3bf3b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_cmp_const.rs
@@ -0,0 +1,58 @@
+// does not test any rustfixable lints
+
+#![warn(clippy::float_cmp_const)]
+#![allow(clippy::float_cmp)]
+#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
+
+const ONE: f32 = 1.0;
+const TWO: f32 = 2.0;
+
+fn eq_one(x: f32) -> bool {
+ if x.is_nan() { false } else { x == ONE } // no error, inside "eq" fn
+}
+
+fn main() {
+ // has errors
+ 1f32 == ONE;
+ TWO == ONE;
+ TWO != ONE;
+ ONE + ONE == TWO;
+ let x = 1;
+ x as f32 == ONE;
+
+ let v = 0.9;
+ v == ONE;
+ v != ONE;
+
+ // no errors, lower than or greater than comparisons
+ v < ONE;
+ v > ONE;
+ v <= ONE;
+ v >= ONE;
+
+ // no errors, zero and infinity values
+ ONE != 0f32;
+ TWO == 0f32;
+ ONE != f32::INFINITY;
+ ONE == f32::NEG_INFINITY;
+
+ // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed
+ let w = 1.1;
+ v == w;
+ v != w;
+ v == 1.0;
+ v != 1.0;
+
+ const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0];
+ const ZERO_INF_ARRAY: [f32; 3] = [0.0, f32::INFINITY, f32::NEG_INFINITY];
+ const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2];
+ const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0];
+
+ // no errors, zero and infinity values
+ NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0
+ ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros
+ ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities
+
+ // has errors
+ NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
+}
diff --git a/src/tools/clippy/tests/ui/float_cmp_const.stderr b/src/tools/clippy/tests/ui/float_cmp_const.stderr
new file mode 100644
index 000000000..d8182cf85
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_cmp_const.stderr
@@ -0,0 +1,67 @@
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:16:5
+ |
+LL | 1f32 == ONE;
+ | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin`
+ |
+ = note: `-D clippy::float-cmp-const` implied by `-D warnings`
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:17:5
+ |
+LL | TWO == ONE;
+ | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:18:5
+ |
+LL | TWO != ONE;
+ | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:19:5
+ |
+LL | ONE + ONE == TWO;
+ | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:21:5
+ |
+LL | x as f32 == ONE;
+ | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:24:5
+ |
+LL | v == ONE;
+ | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant
+ --> $DIR/float_cmp_const.rs:25:5
+ |
+LL | v != ONE;
+ | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin`
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: strict comparison of `f32` or `f64` constant arrays
+ --> $DIR/float_cmp_const.rs:57:5
+ |
+LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs
new file mode 100644
index 000000000..d40fa00c3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_equality_without_abs.rs
@@ -0,0 +1,31 @@
+#![warn(clippy::float_equality_without_abs)]
+
+pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+ (a - b) < f32::EPSILON
+}
+
+pub fn main() {
+ // all errors
+ is_roughly_equal(1.0, 2.0);
+ let a = 0.05;
+ let b = 0.0500001;
+
+ let _ = (a - b) < f32::EPSILON;
+ let _ = a - b < f32::EPSILON;
+ let _ = a - b.abs() < f32::EPSILON;
+ let _ = (a as f64 - b as f64) < f64::EPSILON;
+ let _ = 1.0 - 2.0 < f32::EPSILON;
+
+ let _ = f32::EPSILON > (a - b);
+ let _ = f32::EPSILON > a - b;
+ let _ = f32::EPSILON > a - b.abs();
+ let _ = f64::EPSILON > (a as f64 - b as f64);
+ let _ = f32::EPSILON > 1.0 - 2.0;
+
+ // those are correct
+ let _ = (a - b).abs() < f32::EPSILON;
+ let _ = (a as f64 - b as f64).abs() < f64::EPSILON;
+
+ let _ = f32::EPSILON > (a - b).abs();
+ let _ = f64::EPSILON > (a as f64 - b as f64).abs();
+}
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
new file mode 100644
index 000000000..b34c8159d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
@@ -0,0 +1,92 @@
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:4:5
+ |
+LL | (a - b) < f32::EPSILON
+ | -------^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(a - b).abs()`
+ |
+ = note: `-D clippy::float-equality-without-abs` implied by `-D warnings`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:13:13
+ |
+LL | let _ = (a - b) < f32::EPSILON;
+ | -------^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:14:13
+ |
+LL | let _ = a - b < f32::EPSILON;
+ | -----^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:15:13
+ |
+LL | let _ = a - b.abs() < f32::EPSILON;
+ | -----------^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(a - b.abs()).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:16:13
+ |
+LL | let _ = (a as f64 - b as f64) < f64::EPSILON;
+ | ---------------------^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(a as f64 - b as f64).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:17:13
+ |
+LL | let _ = 1.0 - 2.0 < f32::EPSILON;
+ | ---------^^^^^^^^^^^^^^^
+ | |
+ | help: add `.abs()`: `(1.0 - 2.0).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:19:13
+ |
+LL | let _ = f32::EPSILON > (a - b);
+ | ^^^^^^^^^^^^^^^-------
+ | |
+ | help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:20:13
+ |
+LL | let _ = f32::EPSILON > a - b;
+ | ^^^^^^^^^^^^^^^-----
+ | |
+ | help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:21:13
+ |
+LL | let _ = f32::EPSILON > a - b.abs();
+ | ^^^^^^^^^^^^^^^-----------
+ | |
+ | help: add `.abs()`: `(a - b.abs()).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:22:13
+ |
+LL | let _ = f64::EPSILON > (a as f64 - b as f64);
+ | ^^^^^^^^^^^^^^^---------------------
+ | |
+ | help: add `.abs()`: `(a as f64 - b as f64).abs()`
+
+error: float equality check without `.abs()`
+ --> $DIR/float_equality_without_abs.rs:23:13
+ |
+LL | let _ = f32::EPSILON > 1.0 - 2.0;
+ | ^^^^^^^^^^^^^^^---------
+ | |
+ | help: add `.abs()`: `(1.0 - 2.0).abs()`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed
new file mode 100644
index 000000000..ca747fefc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed
@@ -0,0 +1,84 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+ if num >= 0.0 { num } else { -num }
+}
+
+struct A {
+ a: f64,
+ b: f64,
+}
+
+fn fake_abs1(num: f64) -> f64 {
+ num.abs()
+}
+
+fn fake_abs2(num: f64) -> f64 {
+ num.abs()
+}
+
+fn fake_abs3(a: A) -> f64 {
+ a.a.abs()
+}
+
+fn fake_abs4(num: f64) -> f64 {
+ num.abs()
+}
+
+fn fake_abs5(a: A) -> f64 {
+ a.a.abs()
+}
+
+fn fake_nabs1(num: f64) -> f64 {
+ -num.abs()
+}
+
+fn fake_nabs2(num: f64) -> f64 {
+ -num.abs()
+}
+
+fn fake_nabs3(a: A) -> A {
+ A {
+ a: -a.a.abs(),
+ b: a.b,
+ }
+}
+
+fn not_fake_abs1(num: f64) -> f64 {
+ if num > 0.0 { num } else { -num - 1f64 }
+}
+
+fn not_fake_abs2(num: f64) -> f64 {
+ if num > 0.0 { num + 1.0 } else { -(num + 1.0) }
+}
+
+fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
+ if num1 > 0.0 { num2 } else { -num2 }
+}
+
+fn not_fake_abs4(a: A) -> f64 {
+ if a.a > 0.0 { a.b } else { -a.b }
+}
+
+fn not_fake_abs5(a: A) -> f64 {
+ if a.a > 0.0 { a.a } else { -a.b }
+}
+
+fn main() {
+ fake_abs1(5.0);
+ fake_abs2(5.0);
+ fake_abs3(A { a: 5.0, b: 5.0 });
+ fake_abs4(5.0);
+ fake_abs5(A { a: 5.0, b: 5.0 });
+ fake_nabs1(5.0);
+ fake_nabs2(5.0);
+ fake_nabs3(A { a: 5.0, b: 5.0 });
+ not_fake_abs1(5.0);
+ not_fake_abs2(5.0);
+ not_fake_abs3(5.0, 5.0);
+ not_fake_abs4(A { a: 5.0, b: 5.0 });
+ not_fake_abs5(A { a: 5.0, b: 5.0 });
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs
new file mode 100644
index 000000000..e4b606574
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_abs.rs
@@ -0,0 +1,84 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+ if num >= 0.0 { num } else { -num }
+}
+
+struct A {
+ a: f64,
+ b: f64,
+}
+
+fn fake_abs1(num: f64) -> f64 {
+ if num >= 0.0 { num } else { -num }
+}
+
+fn fake_abs2(num: f64) -> f64 {
+ if 0.0 < num { num } else { -num }
+}
+
+fn fake_abs3(a: A) -> f64 {
+ if a.a > 0.0 { a.a } else { -a.a }
+}
+
+fn fake_abs4(num: f64) -> f64 {
+ if 0.0 >= num { -num } else { num }
+}
+
+fn fake_abs5(a: A) -> f64 {
+ if a.a < 0.0 { -a.a } else { a.a }
+}
+
+fn fake_nabs1(num: f64) -> f64 {
+ if num < 0.0 { num } else { -num }
+}
+
+fn fake_nabs2(num: f64) -> f64 {
+ if 0.0 >= num { num } else { -num }
+}
+
+fn fake_nabs3(a: A) -> A {
+ A {
+ a: if a.a >= 0.0 { -a.a } else { a.a },
+ b: a.b,
+ }
+}
+
+fn not_fake_abs1(num: f64) -> f64 {
+ if num > 0.0 { num } else { -num - 1f64 }
+}
+
+fn not_fake_abs2(num: f64) -> f64 {
+ if num > 0.0 { num + 1.0 } else { -(num + 1.0) }
+}
+
+fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
+ if num1 > 0.0 { num2 } else { -num2 }
+}
+
+fn not_fake_abs4(a: A) -> f64 {
+ if a.a > 0.0 { a.b } else { -a.b }
+}
+
+fn not_fake_abs5(a: A) -> f64 {
+ if a.a > 0.0 { a.a } else { -a.b }
+}
+
+fn main() {
+ fake_abs1(5.0);
+ fake_abs2(5.0);
+ fake_abs3(A { a: 5.0, b: 5.0 });
+ fake_abs4(5.0);
+ fake_abs5(A { a: 5.0, b: 5.0 });
+ fake_nabs1(5.0);
+ fake_nabs2(5.0);
+ fake_nabs3(A { a: 5.0, b: 5.0 });
+ not_fake_abs1(5.0);
+ not_fake_abs2(5.0);
+ not_fake_abs3(5.0, 5.0);
+ not_fake_abs4(A { a: 5.0, b: 5.0 });
+ not_fake_abs5(A { a: 5.0, b: 5.0 });
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr
new file mode 100644
index 000000000..db8290423
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr
@@ -0,0 +1,52 @@
+error: manual implementation of `abs` method
+ --> $DIR/floating_point_abs.rs:16:5
+ |
+LL | if num >= 0.0 { num } else { -num }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: manual implementation of `abs` method
+ --> $DIR/floating_point_abs.rs:20:5
+ |
+LL | if 0.0 < num { num } else { -num }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
+
+error: manual implementation of `abs` method
+ --> $DIR/floating_point_abs.rs:24:5
+ |
+LL | if a.a > 0.0 { a.a } else { -a.a }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
+
+error: manual implementation of `abs` method
+ --> $DIR/floating_point_abs.rs:28:5
+ |
+LL | if 0.0 >= num { -num } else { num }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
+
+error: manual implementation of `abs` method
+ --> $DIR/floating_point_abs.rs:32:5
+ |
+LL | if a.a < 0.0 { -a.a } else { a.a }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
+
+error: manual implementation of negation of `abs` method
+ --> $DIR/floating_point_abs.rs:36:5
+ |
+LL | if num < 0.0 { num } else { -num }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
+
+error: manual implementation of negation of `abs` method
+ --> $DIR/floating_point_abs.rs:40:5
+ |
+LL | if 0.0 >= num { num } else { -num }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
+
+error: manual implementation of negation of `abs` method
+ --> $DIR/floating_point_abs.rs:45:12
+ |
+LL | a: if a.a >= 0.0 { -a.a } else { a.a },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_exp.fixed b/src/tools/clippy/tests/ui/floating_point_exp.fixed
new file mode 100644
index 000000000..ae7805fdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_exp.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+ let x = 2f32;
+ let _ = x.exp_m1();
+ let _ = x.exp_m1() + 2.0;
+ // Cases where the lint shouldn't be applied
+ let _ = x.exp() - 2.0;
+ let _ = x.exp() - 1.0 * 2.0;
+
+ let x = 2f64;
+ let _ = x.exp_m1();
+ let _ = x.exp_m1() + 2.0;
+ // Cases where the lint shouldn't be applied
+ let _ = x.exp() - 2.0;
+ let _ = x.exp() - 1.0 * 2.0;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_exp.rs b/src/tools/clippy/tests/ui/floating_point_exp.rs
new file mode 100644
index 000000000..27e0b9bcb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_exp.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+ let x = 2f32;
+ let _ = x.exp() - 1.0;
+ let _ = x.exp() - 1.0 + 2.0;
+ // Cases where the lint shouldn't be applied
+ let _ = x.exp() - 2.0;
+ let _ = x.exp() - 1.0 * 2.0;
+
+ let x = 2f64;
+ let _ = x.exp() - 1.0;
+ let _ = x.exp() - 1.0 + 2.0;
+ // Cases where the lint shouldn't be applied
+ let _ = x.exp() - 2.0;
+ let _ = x.exp() - 1.0 * 2.0;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_exp.stderr b/src/tools/clippy/tests/ui/floating_point_exp.stderr
new file mode 100644
index 000000000..5cd999ad4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_exp.stderr
@@ -0,0 +1,28 @@
+error: (e.pow(x) - 1) can be computed more accurately
+ --> $DIR/floating_point_exp.rs:6:13
+ |
+LL | let _ = x.exp() - 1.0;
+ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+ |
+ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: (e.pow(x) - 1) can be computed more accurately
+ --> $DIR/floating_point_exp.rs:7:13
+ |
+LL | let _ = x.exp() - 1.0 + 2.0;
+ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+
+error: (e.pow(x) - 1) can be computed more accurately
+ --> $DIR/floating_point_exp.rs:13:13
+ |
+LL | let _ = x.exp() - 1.0;
+ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+
+error: (e.pow(x) - 1) can be computed more accurately
+ --> $DIR/floating_point_exp.rs:14:13
+ |
+LL | let _ = x.exp() - 1.0 + 2.0;
+ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.fixed b/src/tools/clippy/tests/ui/floating_point_hypot.fixed
new file mode 100644
index 000000000..bbe411b3f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+ let x = 3f32;
+ let y = 4f32;
+ let _ = x.hypot(y);
+ let _ = (x + 1f32).hypot(y);
+ let _ = x.hypot(y);
+ // Cases where the lint shouldn't be applied
+ // TODO: linting this adds some complexity, but could be done
+ let _ = x.mul_add(x, y * y).sqrt();
+ let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.rs b/src/tools/clippy/tests/ui/floating_point_hypot.rs
new file mode 100644
index 000000000..586fd170e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+ let x = 3f32;
+ let y = 4f32;
+ let _ = (x * x + y * y).sqrt();
+ let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+ let _ = (x.powi(2) + y.powi(2)).sqrt();
+ // Cases where the lint shouldn't be applied
+ // TODO: linting this adds some complexity, but could be done
+ let _ = x.mul_add(x, y * y).sqrt();
+ let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.stderr b/src/tools/clippy/tests/ui/floating_point_hypot.stderr
new file mode 100644
index 000000000..42069d9ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.stderr
@@ -0,0 +1,22 @@
+error: hypotenuse can be computed more accurately
+ --> $DIR/floating_point_hypot.rs:7:13
+ |
+LL | let _ = (x * x + y * y).sqrt();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+ |
+ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: hypotenuse can be computed more accurately
+ --> $DIR/floating_point_hypot.rs:8:13
+ |
+LL | let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)`
+
+error: hypotenuse can be computed more accurately
+ --> $DIR/floating_point_hypot.rs:9:13
+ |
+LL | let _ = (x.powi(2) + y.powi(2)).sqrt();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed
new file mode 100644
index 000000000..5b487bb8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_log.fixed
@@ -0,0 +1,58 @@
+// run-rustfix
+#![allow(dead_code, clippy::double_parens)]
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+const TWO: f32 = 2.0;
+const E: f32 = std::f32::consts::E;
+
+fn check_log_base() {
+ let x = 1f32;
+ let _ = x.log2();
+ let _ = x.log10();
+ let _ = x.ln();
+ let _ = x.log2();
+ let _ = x.ln();
+
+ let x = 1f64;
+ let _ = x.log2();
+ let _ = x.log10();
+ let _ = x.ln();
+}
+
+fn check_ln1p() {
+ let x = 1f32;
+ let _ = 2.0f32.ln_1p();
+ let _ = 2.0f32.ln_1p();
+ let _ = x.ln_1p();
+ let _ = (x / 2.0).ln_1p();
+ let _ = x.powi(3).ln_1p();
+ let _ = (x.powi(3) / 2.0).ln_1p();
+ let _ = (std::f32::consts::E - 1.0).ln_1p();
+ let _ = x.ln_1p();
+ let _ = x.powi(3).ln_1p();
+ let _ = (x + 2.0).ln_1p();
+ let _ = (x / 2.0).ln_1p();
+ // Cases where the lint shouldn't be applied
+ let _ = (1.0 + x + 2.0).ln();
+ let _ = (x + 1.0 + 2.0).ln();
+ let _ = (x + 1.0 / 2.0).ln();
+ let _ = (1.0 + x - 2.0).ln();
+
+ let x = 1f64;
+ let _ = 2.0f64.ln_1p();
+ let _ = 2.0f64.ln_1p();
+ let _ = x.ln_1p();
+ let _ = (x / 2.0).ln_1p();
+ let _ = x.powi(3).ln_1p();
+ let _ = x.ln_1p();
+ let _ = x.powi(3).ln_1p();
+ let _ = (x + 2.0).ln_1p();
+ let _ = (x / 2.0).ln_1p();
+ // Cases where the lint shouldn't be applied
+ let _ = (1.0 + x + 2.0).ln();
+ let _ = (x + 1.0 + 2.0).ln();
+ let _ = (x + 1.0 / 2.0).ln();
+ let _ = (1.0 + x - 2.0).ln();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs
new file mode 100644
index 000000000..01181484e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_log.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+#![allow(dead_code, clippy::double_parens)]
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+const TWO: f32 = 2.0;
+const E: f32 = std::f32::consts::E;
+
+fn check_log_base() {
+ let x = 1f32;
+ let _ = x.log(2f32);
+ let _ = x.log(10f32);
+ let _ = x.log(std::f32::consts::E);
+ let _ = x.log(TWO);
+ let _ = x.log(E);
+
+ let x = 1f64;
+ let _ = x.log(2f64);
+ let _ = x.log(10f64);
+ let _ = x.log(std::f64::consts::E);
+}
+
+fn check_ln1p() {
+ let x = 1f32;
+ let _ = (1f32 + 2.).ln();
+ let _ = (1f32 + 2.0).ln();
+ let _ = (1.0 + x).ln();
+ let _ = (1.0 + x / 2.0).ln();
+ let _ = (1.0 + x.powi(3)).ln();
+ let _ = (1.0 + x.powi(3) / 2.0).ln();
+ let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
+ let _ = (x + 1.0).ln();
+ let _ = (x.powi(3) + 1.0).ln();
+ let _ = (x + 2.0 + 1.0).ln();
+ let _ = (x / 2.0 + 1.0).ln();
+ // Cases where the lint shouldn't be applied
+ let _ = (1.0 + x + 2.0).ln();
+ let _ = (x + 1.0 + 2.0).ln();
+ let _ = (x + 1.0 / 2.0).ln();
+ let _ = (1.0 + x - 2.0).ln();
+
+ let x = 1f64;
+ let _ = (1f64 + 2.).ln();
+ let _ = (1f64 + 2.0).ln();
+ let _ = (1.0 + x).ln();
+ let _ = (1.0 + x / 2.0).ln();
+ let _ = (1.0 + x.powi(3)).ln();
+ let _ = (x + 1.0).ln();
+ let _ = (x.powi(3) + 1.0).ln();
+ let _ = (x + 2.0 + 1.0).ln();
+ let _ = (x / 2.0 + 1.0).ln();
+ // Cases where the lint shouldn't be applied
+ let _ = (1.0 + x + 2.0).ln();
+ let _ = (x + 1.0 + 2.0).ln();
+ let _ = (x + 1.0 / 2.0).ln();
+ let _ = (1.0 + x - 2.0).ln();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr
new file mode 100644
index 000000000..96e5a1544
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_log.stderr
@@ -0,0 +1,174 @@
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:10:13
+ |
+LL | let _ = x.log(2f32);
+ | ^^^^^^^^^^^ help: consider using: `x.log2()`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:11:13
+ |
+LL | let _ = x.log(10f32);
+ | ^^^^^^^^^^^^ help: consider using: `x.log10()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:12:13
+ |
+LL | let _ = x.log(std::f32::consts::E);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:13:13
+ |
+LL | let _ = x.log(TWO);
+ | ^^^^^^^^^^ help: consider using: `x.log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:14:13
+ |
+LL | let _ = x.log(E);
+ | ^^^^^^^^ help: consider using: `x.ln()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:17:13
+ |
+LL | let _ = x.log(2f64);
+ | ^^^^^^^^^^^ help: consider using: `x.log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:18:13
+ |
+LL | let _ = x.log(10f64);
+ | ^^^^^^^^^^^^ help: consider using: `x.log10()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+ --> $DIR/floating_point_log.rs:19:13
+ |
+LL | let _ = x.log(std::f64::consts::E);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:24:13
+ |
+LL | let _ = (1f32 + 2.).ln();
+ | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
+ |
+ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:25:13
+ |
+LL | let _ = (1f32 + 2.0).ln();
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:26:13
+ |
+LL | let _ = (1.0 + x).ln();
+ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:27:13
+ |
+LL | let _ = (1.0 + x / 2.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:28:13
+ |
+LL | let _ = (1.0 + x.powi(3)).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:29:13
+ |
+LL | let _ = (1.0 + x.powi(3) / 2.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:30:13
+ |
+LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:31:13
+ |
+LL | let _ = (x + 1.0).ln();
+ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:32:13
+ |
+LL | let _ = (x.powi(3) + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:33:13
+ |
+LL | let _ = (x + 2.0 + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:34:13
+ |
+LL | let _ = (x / 2.0 + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:42:13
+ |
+LL | let _ = (1f64 + 2.).ln();
+ | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:43:13
+ |
+LL | let _ = (1f64 + 2.0).ln();
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:44:13
+ |
+LL | let _ = (1.0 + x).ln();
+ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:45:13
+ |
+LL | let _ = (1.0 + x / 2.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:46:13
+ |
+LL | let _ = (1.0 + x.powi(3)).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:47:13
+ |
+LL | let _ = (x + 1.0).ln();
+ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:48:13
+ |
+LL | let _ = (x.powi(3) + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:49:13
+ |
+LL | let _ = (x + 2.0 + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
+
+error: ln(1 + x) can be computed more accurately
+ --> $DIR/floating_point_log.rs:50:13
+ |
+LL | let _ = (x / 2.0 + 1.0).ln();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
+
+error: aborting due to 28 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.fixed b/src/tools/clippy/tests/ui/floating_point_logbase.fixed
new file mode 100644
index 000000000..13962a272
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+ let x = 3f32;
+ let y = 5f32;
+ let _ = x.log(y);
+ let _ = x.log(y);
+ let _ = x.log(y);
+ let _ = x.log(y);
+ // Cases where the lint shouldn't be applied
+ let _ = x.ln() / y.powf(3.2);
+ let _ = x.powf(3.2) / y.powf(3.2);
+ let _ = x.powf(3.2) / y.ln();
+ let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.rs b/src/tools/clippy/tests/ui/floating_point_logbase.rs
new file mode 100644
index 000000000..26bc20d53
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+ let x = 3f32;
+ let y = 5f32;
+ let _ = x.ln() / y.ln();
+ let _ = x.log2() / y.log2();
+ let _ = x.log10() / y.log10();
+ let _ = x.log(5f32) / y.log(5f32);
+ // Cases where the lint shouldn't be applied
+ let _ = x.ln() / y.powf(3.2);
+ let _ = x.powf(3.2) / y.powf(3.2);
+ let _ = x.powf(3.2) / y.ln();
+ let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.stderr b/src/tools/clippy/tests/ui/floating_point_logbase.stderr
new file mode 100644
index 000000000..78354c2f6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.stderr
@@ -0,0 +1,28 @@
+error: log base can be expressed more clearly
+ --> $DIR/floating_point_logbase.rs:7:13
+ |
+LL | let _ = x.ln() / y.ln();
+ | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: log base can be expressed more clearly
+ --> $DIR/floating_point_logbase.rs:8:13
+ |
+LL | let _ = x.log2() / y.log2();
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+ --> $DIR/floating_point_logbase.rs:9:13
+ |
+LL | let _ = x.log10() / y.log10();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+ --> $DIR/floating_point_logbase.rs:10:13
+ |
+LL | let _ = x.log(5f32) / y.log(5f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
new file mode 100644
index 000000000..169ec02f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -0,0 +1,37 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+
+ let _ = a * b + c;
+ let _ = c + a * b;
+}
+
+fn main() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+ let d: f64 = 0.0001;
+
+ let _ = a.mul_add(b, c);
+ let _ = a.mul_add(b, c);
+ let _ = 2.0f64.mul_add(4.0, a);
+ let _ = 2.0f64.mul_add(4., a);
+
+ let _ = a.mul_add(b, c);
+ let _ = a.mul_add(b, c);
+ let _ = (a * b).mul_add(c, d);
+
+ let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
+ let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
+
+ let _ = a.mul_add(a, b).sqrt();
+
+ // Cases where the lint shouldn't be applied
+ let _ = (a * a + b * b).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
new file mode 100644
index 000000000..5338d4fc2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+
+ let _ = a * b + c;
+ let _ = c + a * b;
+}
+
+fn main() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+ let d: f64 = 0.0001;
+
+ let _ = a * b + c;
+ let _ = c + a * b;
+ let _ = a + 2.0 * 4.0;
+ let _ = a + 2. * 4.;
+
+ let _ = (a * b) + c;
+ let _ = c + (a * b);
+ let _ = a * b * c + d;
+
+ let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
+ let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+
+ let _ = (a * a + b).sqrt();
+
+ // Cases where the lint shouldn't be applied
+ let _ = (a * a + b * b).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
new file mode 100644
index 000000000..e637bbf90
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -0,0 +1,64 @@
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:21:13
+ |
+LL | let _ = a * b + c;
+ | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:22:13
+ |
+LL | let _ = c + a * b;
+ | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:23:13
+ |
+LL | let _ = a + 2.0 * 4.0;
+ | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:24:13
+ |
+LL | let _ = a + 2. * 4.;
+ | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:26:13
+ |
+LL | let _ = (a * b) + c;
+ | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:27:13
+ |
+LL | let _ = c + (a * b);
+ | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:28:13
+ |
+LL | let _ = a * b * c + d;
+ | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:30:13
+ |
+LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:31:13
+ |
+LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_mul_add.rs:33:13
+ |
+LL | let _ = (a * a + b).sqrt();
+ | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed
new file mode 100644
index 000000000..b0641a100
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+ let x = 3f32;
+ let _ = x.exp2();
+ let _ = 3.1f32.exp2();
+ let _ = (-3.1f32).exp2();
+ let _ = x.exp();
+ let _ = 3.1f32.exp();
+ let _ = (-3.1f32).exp();
+ let _ = x.sqrt();
+ let _ = x.cbrt();
+ let _ = x.powi(3);
+ let _ = x.powi(-2);
+ let _ = x.powi(16_777_215);
+ let _ = x.powi(-16_777_215);
+ // Cases where the lint shouldn't be applied
+ let _ = x.powf(2.1);
+ let _ = x.powf(-2.1);
+ let _ = x.powf(16_777_216.0);
+ let _ = x.powf(-16_777_216.0);
+
+ let x = 3f64;
+ let _ = x.exp2();
+ let _ = 3.1f64.exp2();
+ let _ = (-3.1f64).exp2();
+ let _ = x.exp();
+ let _ = 3.1f64.exp();
+ let _ = (-3.1f64).exp();
+ let _ = x.sqrt();
+ let _ = x.cbrt();
+ let _ = x.powi(3);
+ let _ = x.powi(-2);
+ let _ = x.powi(-2_147_483_648);
+ let _ = x.powi(2_147_483_647);
+ // Cases where the lint shouldn't be applied
+ let _ = x.powf(2.1);
+ let _ = x.powf(-2.1);
+ let _ = x.powf(-2_147_483_649.0);
+ let _ = x.powf(2_147_483_648.0);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs
new file mode 100644
index 000000000..a0a2c9739
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powf.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+ let x = 3f32;
+ let _ = 2f32.powf(x);
+ let _ = 2f32.powf(3.1);
+ let _ = 2f32.powf(-3.1);
+ let _ = std::f32::consts::E.powf(x);
+ let _ = std::f32::consts::E.powf(3.1);
+ let _ = std::f32::consts::E.powf(-3.1);
+ let _ = x.powf(1.0 / 2.0);
+ let _ = x.powf(1.0 / 3.0);
+ let _ = x.powf(3.0);
+ let _ = x.powf(-2.0);
+ let _ = x.powf(16_777_215.0);
+ let _ = x.powf(-16_777_215.0);
+ // Cases where the lint shouldn't be applied
+ let _ = x.powf(2.1);
+ let _ = x.powf(-2.1);
+ let _ = x.powf(16_777_216.0);
+ let _ = x.powf(-16_777_216.0);
+
+ let x = 3f64;
+ let _ = 2f64.powf(x);
+ let _ = 2f64.powf(3.1);
+ let _ = 2f64.powf(-3.1);
+ let _ = std::f64::consts::E.powf(x);
+ let _ = std::f64::consts::E.powf(3.1);
+ let _ = std::f64::consts::E.powf(-3.1);
+ let _ = x.powf(1.0 / 2.0);
+ let _ = x.powf(1.0 / 3.0);
+ let _ = x.powf(3.0);
+ let _ = x.powf(-2.0);
+ let _ = x.powf(-2_147_483_648.0);
+ let _ = x.powf(2_147_483_647.0);
+ // Cases where the lint shouldn't be applied
+ let _ = x.powf(2.1);
+ let _ = x.powf(-2.1);
+ let _ = x.powf(-2_147_483_649.0);
+ let _ = x.powf(2_147_483_648.0);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr
new file mode 100644
index 000000000..2422eb911
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr
@@ -0,0 +1,150 @@
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:6:13
+ |
+LL | let _ = 2f32.powf(x);
+ | ^^^^^^^^^^^^ help: consider using: `x.exp2()`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:7:13
+ |
+LL | let _ = 2f32.powf(3.1);
+ | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:8:13
+ |
+LL | let _ = 2f32.powf(-3.1);
+ | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:9:13
+ |
+LL | let _ = std::f32::consts::E.powf(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:10:13
+ |
+LL | let _ = std::f32::consts::E.powf(3.1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:11:13
+ |
+LL | let _ = std::f32::consts::E.powf(-3.1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
+
+error: square-root of a number can be computed more efficiently and accurately
+ --> $DIR/floating_point_powf.rs:12:13
+ |
+LL | let _ = x.powf(1.0 / 2.0);
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
+
+error: cube-root of a number can be computed more accurately
+ --> $DIR/floating_point_powf.rs:13:13
+ |
+LL | let _ = x.powf(1.0 / 3.0);
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
+ |
+ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:14:13
+ |
+LL | let _ = x.powf(3.0);
+ | ^^^^^^^^^^^ help: consider using: `x.powi(3)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:15:13
+ |
+LL | let _ = x.powf(-2.0);
+ | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:16:13
+ |
+LL | let _ = x.powf(16_777_215.0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:17:13
+ |
+LL | let _ = x.powf(-16_777_215.0);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:25:13
+ |
+LL | let _ = 2f64.powf(x);
+ | ^^^^^^^^^^^^ help: consider using: `x.exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:26:13
+ |
+LL | let _ = 2f64.powf(3.1);
+ | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:27:13
+ |
+LL | let _ = 2f64.powf(-3.1);
+ | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:28:13
+ |
+LL | let _ = std::f64::consts::E.powf(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:29:13
+ |
+LL | let _ = std::f64::consts::E.powf(3.1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
+
+error: exponent for bases 2 and e can be computed more accurately
+ --> $DIR/floating_point_powf.rs:30:13
+ |
+LL | let _ = std::f64::consts::E.powf(-3.1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
+
+error: square-root of a number can be computed more efficiently and accurately
+ --> $DIR/floating_point_powf.rs:31:13
+ |
+LL | let _ = x.powf(1.0 / 2.0);
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
+
+error: cube-root of a number can be computed more accurately
+ --> $DIR/floating_point_powf.rs:32:13
+ |
+LL | let _ = x.powf(1.0 / 3.0);
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:33:13
+ |
+LL | let _ = x.powf(3.0);
+ | ^^^^^^^^^^^ help: consider using: `x.powi(3)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:34:13
+ |
+LL | let _ = x.powf(-2.0);
+ | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:35:13
+ |
+LL | let _ = x.powf(-2_147_483_648.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
+
+error: exponentiation with integer powers can be computed more efficiently
+ --> $DIR/floating_point_powf.rs:36:13
+ |
+LL | let _ = x.powf(2_147_483_647.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.fixed b/src/tools/clippy/tests/ui/floating_point_powi.fixed
new file mode 100644
index 000000000..85f7c531e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.fixed
@@ -0,0 +1,20 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+ let one = 1;
+ let x = 3f32;
+
+ let y = 4f32;
+ let _ = x.mul_add(x, y);
+ let _ = y.mul_add(y, x);
+ let _ = x.mul_add(x, y).sqrt();
+ let _ = y.mul_add(y, x).sqrt();
+ // Cases where the lint shouldn't be applied
+ let _ = x.powi(2);
+ let _ = x.powi(1 + 1);
+ let _ = x.powi(3);
+ let _ = x.powi(4) + y;
+ let _ = x.powi(one + 1);
+ let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.rs b/src/tools/clippy/tests/ui/floating_point_powi.rs
new file mode 100644
index 000000000..ece61d1be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+ let one = 1;
+ let x = 3f32;
+
+ let y = 4f32;
+ let _ = x.powi(2) + y;
+ let _ = x + y.powi(2);
+ let _ = (x.powi(2) + y).sqrt();
+ let _ = (x + y.powi(2)).sqrt();
+ // Cases where the lint shouldn't be applied
+ let _ = x.powi(2);
+ let _ = x.powi(1 + 1);
+ let _ = x.powi(3);
+ let _ = x.powi(4) + y;
+ let _ = x.powi(one + 1);
+ let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.stderr b/src/tools/clippy/tests/ui/floating_point_powi.stderr
new file mode 100644
index 000000000..37d840988
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.stderr
@@ -0,0 +1,28 @@
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_powi.rs:9:13
+ |
+LL | let _ = x.powi(2) + y;
+ | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_powi.rs:10:13
+ |
+LL | let _ = x + y.powi(2);
+ | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_powi.rs:11:13
+ |
+LL | let _ = (x.powi(2) + y).sqrt();
+ | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+ --> $DIR/floating_point_powi.rs:12:13
+ |
+LL | let _ = (x + y.powi(2)).sqrt();
+ | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.fixed b/src/tools/clippy/tests/ui/floating_point_rad.fixed
new file mode 100644
index 000000000..ce91fe176
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+ let x = 3f32;
+ let _ = x * 180f32 / std::f32::consts::PI;
+}
+
+fn main() {
+ let x = 3f32;
+ let _ = x.to_degrees();
+ let _ = 90.0_f64.to_degrees();
+ let _ = 90.5_f64.to_degrees();
+ let _ = x.to_radians();
+ let _ = 90.0_f64.to_radians();
+ let _ = 90.5_f64.to_radians();
+ // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
+ // Cases where the lint shouldn't be applied
+ let _ = x * 90f32 / std::f32::consts::PI;
+ let _ = x * std::f32::consts::PI / 90f32;
+ let _ = x * 180f32 / std::f32::consts::E;
+ let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.rs b/src/tools/clippy/tests/ui/floating_point_rad.rs
new file mode 100644
index 000000000..8f3234986
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
+#![warn(clippy::suboptimal_flops)]
+
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+ let x = 3f32;
+ let _ = x * 180f32 / std::f32::consts::PI;
+}
+
+fn main() {
+ let x = 3f32;
+ let _ = x * 180f32 / std::f32::consts::PI;
+ let _ = 90. * 180f64 / std::f64::consts::PI;
+ let _ = 90.5 * 180f64 / std::f64::consts::PI;
+ let _ = x * std::f32::consts::PI / 180f32;
+ let _ = 90. * std::f32::consts::PI / 180f32;
+ let _ = 90.5 * std::f32::consts::PI / 180f32;
+ // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
+ // Cases where the lint shouldn't be applied
+ let _ = x * 90f32 / std::f32::consts::PI;
+ let _ = x * std::f32::consts::PI / 90f32;
+ let _ = x * 180f32 / std::f32::consts::E;
+ let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.stderr b/src/tools/clippy/tests/ui/floating_point_rad.stderr
new file mode 100644
index 000000000..f12d3d23f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.stderr
@@ -0,0 +1,40 @@
+error: conversion to degrees can be done more accurately
+ --> $DIR/floating_point_rad.rs:13:13
+ |
+LL | let _ = x * 180f32 / std::f32::consts::PI;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
+ |
+ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: conversion to degrees can be done more accurately
+ --> $DIR/floating_point_rad.rs:14:13
+ |
+LL | let _ = 90. * 180f64 / std::f64::consts::PI;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
+
+error: conversion to degrees can be done more accurately
+ --> $DIR/floating_point_rad.rs:15:13
+ |
+LL | let _ = 90.5 * 180f64 / std::f64::consts::PI;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
+
+error: conversion to radians can be done more accurately
+ --> $DIR/floating_point_rad.rs:16:13
+ |
+LL | let _ = x * std::f32::consts::PI / 180f32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
+
+error: conversion to radians can be done more accurately
+ --> $DIR/floating_point_rad.rs:17:13
+ |
+LL | let _ = 90. * std::f32::consts::PI / 180f32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
+
+error: conversion to radians can be done more accurately
+ --> $DIR/floating_point_rad.rs:18:13
+ |
+LL | let _ = 90.5 * std::f32::consts::PI / 180f32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.rs b/src/tools/clippy/tests/ui/fn_address_comparisons.rs
new file mode 100644
index 000000000..362dcb4fd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_address_comparisons.rs
@@ -0,0 +1,20 @@
+use std::fmt::Debug;
+use std::ptr;
+use std::rc::Rc;
+use std::sync::Arc;
+
+fn a() {}
+
+#[warn(clippy::fn_address_comparisons)]
+fn main() {
+ type F = fn();
+ let f: F = a;
+ let g: F = f;
+
+ // These should fail:
+ let _ = f == a;
+ let _ = f != a;
+
+ // These should be fine:
+ let _ = f == g;
+}
diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.stderr b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr
new file mode 100644
index 000000000..9c1b5419a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr
@@ -0,0 +1,16 @@
+error: comparing with a non-unique address of a function item
+ --> $DIR/fn_address_comparisons.rs:15:13
+ |
+LL | let _ = f == a;
+ | ^^^^^^
+ |
+ = note: `-D clippy::fn-address-comparisons` implied by `-D warnings`
+
+error: comparing with a non-unique address of a function item
+ --> $DIR/fn_address_comparisons.rs:16:13
+ |
+LL | let _ = f != a;
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs
new file mode 100644
index 000000000..f805bcc9b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs
@@ -0,0 +1,45 @@
+#![warn(clippy::fn_params_excessive_bools)]
+#![allow(clippy::too_many_arguments)]
+
+extern "C" {
+ fn f(_: bool, _: bool, _: bool, _: bool);
+}
+
+macro_rules! foo {
+ () => {
+ fn fff(_: bool, _: bool, _: bool, _: bool) {}
+ };
+}
+
+foo!();
+
+#[no_mangle]
+extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {}
+fn g(_: bool, _: bool, _: bool, _: bool) {}
+fn h(_: bool, _: bool, _: bool) {}
+fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
+fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
+
+struct S;
+trait Trait {
+ fn f(_: bool, _: bool, _: bool, _: bool);
+ fn g(_: bool, _: bool, _: bool, _: Vec<u32>);
+}
+
+impl S {
+ fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
+ fn g(&self, _: bool, _: bool, _: bool) {}
+ #[no_mangle]
+ extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {}
+}
+
+impl Trait for S {
+ fn f(_: bool, _: bool, _: bool, _: bool) {}
+ fn g(_: bool, _: bool, _: bool, _: Vec<u32>) {}
+}
+
+fn main() {
+ fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
+ fn nn(_: bool, _: bool, _: bool, _: bool) {}
+ }
+}
diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr
new file mode 100644
index 000000000..cd9d07fa1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr
@@ -0,0 +1,53 @@
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:18:1
+ |
+LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
+ = help: consider refactoring bools into two-variant enums
+
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:21:1
+ |
+LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider refactoring bools into two-variant enums
+
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:25:5
+ |
+LL | fn f(_: bool, _: bool, _: bool, _: bool);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider refactoring bools into two-variant enums
+
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:30:5
+ |
+LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider refactoring bools into two-variant enums
+
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:42:5
+ |
+LL | / fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
+LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {}
+LL | | }
+ | |_____^
+ |
+ = help: consider refactoring bools into two-variant enums
+
+error: more than 3 bools in function parameters
+ --> $DIR/fn_params_excessive_bools.rs:43:9
+ |
+LL | fn nn(_: bool, _: bool, _: bool, _: bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider refactoring bools into two-variant enums
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
new file mode 100644
index 000000000..a456c085c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
@@ -0,0 +1,55 @@
+// ignore-32bit
+
+#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
+
+fn foo() -> String {
+ String::new()
+}
+
+fn test_function_to_numeric_cast() {
+ let _ = foo as i8;
+ let _ = foo as i16;
+ let _ = foo as i32;
+ let _ = foo as i64;
+ let _ = foo as i128;
+ let _ = foo as isize;
+
+ let _ = foo as u8;
+ let _ = foo as u16;
+ let _ = foo as u32;
+ let _ = foo as u64;
+ let _ = foo as u128;
+
+ // Casting to usize is OK and should not warn
+ let _ = foo as usize;
+
+ // Cast `f` (a `FnDef`) to `fn()` should not warn
+ fn f() {}
+ let _ = f as fn();
+}
+
+fn test_function_var_to_numeric_cast() {
+ let abc: fn() -> String = foo;
+
+ let _ = abc as i8;
+ let _ = abc as i16;
+ let _ = abc as i32;
+ let _ = abc as i64;
+ let _ = abc as i128;
+ let _ = abc as isize;
+
+ let _ = abc as u8;
+ let _ = abc as u16;
+ let _ = abc as u32;
+ let _ = abc as u64;
+ let _ = abc as u128;
+
+ // Casting to usize is OK and should not warn
+ let _ = abc as usize;
+}
+
+fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
+ f as i32
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr
new file mode 100644
index 000000000..e9549e157
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr
@@ -0,0 +1,144 @@
+error: casting function pointer `foo` to `i8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:10:13
+ |
+LL | let _ = foo as i8;
+ | ^^^^^^^^^ help: try: `foo as usize`
+ |
+ = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
+
+error: casting function pointer `foo` to `i16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:11:13
+ |
+LL | let _ = foo as i16;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `i32`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:12:13
+ |
+LL | let _ = foo as i32;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `i64`
+ --> $DIR/fn_to_numeric_cast.rs:13:13
+ |
+LL | let _ = foo as i64;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+ |
+ = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
+
+error: casting function pointer `foo` to `i128`
+ --> $DIR/fn_to_numeric_cast.rs:14:13
+ |
+LL | let _ = foo as i128;
+ | ^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `isize`
+ --> $DIR/fn_to_numeric_cast.rs:15:13
+ |
+LL | let _ = foo as isize;
+ | ^^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:17:13
+ |
+LL | let _ = foo as u8;
+ | ^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:18:13
+ |
+LL | let _ = foo as u16;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u32`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:19:13
+ |
+LL | let _ = foo as u32;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u64`
+ --> $DIR/fn_to_numeric_cast.rs:20:13
+ |
+LL | let _ = foo as u64;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u128`
+ --> $DIR/fn_to_numeric_cast.rs:21:13
+ |
+LL | let _ = foo as u128;
+ | ^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `abc` to `i8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:34:13
+ |
+LL | let _ = abc as i8;
+ | ^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:35:13
+ |
+LL | let _ = abc as i16;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i32`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:36:13
+ |
+LL | let _ = abc as i32;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i64`
+ --> $DIR/fn_to_numeric_cast.rs:37:13
+ |
+LL | let _ = abc as i64;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i128`
+ --> $DIR/fn_to_numeric_cast.rs:38:13
+ |
+LL | let _ = abc as i128;
+ | ^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `isize`
+ --> $DIR/fn_to_numeric_cast.rs:39:13
+ |
+LL | let _ = abc as isize;
+ | ^^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:41:13
+ |
+LL | let _ = abc as u8;
+ | ^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:42:13
+ |
+LL | let _ = abc as u16;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u32`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:43:13
+ |
+LL | let _ = abc as u32;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u64`
+ --> $DIR/fn_to_numeric_cast.rs:44:13
+ |
+LL | let _ = abc as u64;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u128`
+ --> $DIR/fn_to_numeric_cast.rs:45:13
+ |
+LL | let _ = abc as u128;
+ | ^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `f` to `i32`, which truncates the value
+ --> $DIR/fn_to_numeric_cast.rs:52:5
+ |
+LL | f as i32
+ | ^^^^^^^^ help: try: `f as usize`
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs
new file mode 100644
index 000000000..04ee985c0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs
@@ -0,0 +1,55 @@
+// ignore-64bit
+
+#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
+
+fn foo() -> String {
+ String::new()
+}
+
+fn test_function_to_numeric_cast() {
+ let _ = foo as i8;
+ let _ = foo as i16;
+ let _ = foo as i32;
+ let _ = foo as i64;
+ let _ = foo as i128;
+ let _ = foo as isize;
+
+ let _ = foo as u8;
+ let _ = foo as u16;
+ let _ = foo as u32;
+ let _ = foo as u64;
+ let _ = foo as u128;
+
+ // Casting to usize is OK and should not warn
+ let _ = foo as usize;
+
+ // Cast `f` (a `FnDef`) to `fn()` should not warn
+ fn f() {}
+ let _ = f as fn();
+}
+
+fn test_function_var_to_numeric_cast() {
+ let abc: fn() -> String = foo;
+
+ let _ = abc as i8;
+ let _ = abc as i16;
+ let _ = abc as i32;
+ let _ = abc as i64;
+ let _ = abc as i128;
+ let _ = abc as isize;
+
+ let _ = abc as u8;
+ let _ = abc as u16;
+ let _ = abc as u32;
+ let _ = abc as u64;
+ let _ = abc as u128;
+
+ // Casting to usize is OK and should not warn
+ let _ = abc as usize;
+}
+
+fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
+ f as i32
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr
new file mode 100644
index 000000000..08dd611d6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr
@@ -0,0 +1,144 @@
+error: casting function pointer `foo` to `i8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:10:13
+ |
+LL | let _ = foo as i8;
+ | ^^^^^^^^^ help: try: `foo as usize`
+ |
+ = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
+
+error: casting function pointer `foo` to `i16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:11:13
+ |
+LL | let _ = foo as i16;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `i32`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:12:13
+ |
+LL | let _ = foo as i32;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+ |
+ = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
+
+error: casting function pointer `foo` to `i64`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:13:13
+ |
+LL | let _ = foo as i64;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `i128`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:14:13
+ |
+LL | let _ = foo as i128;
+ | ^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `isize`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:15:13
+ |
+LL | let _ = foo as isize;
+ | ^^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:17:13
+ |
+LL | let _ = foo as u8;
+ | ^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:18:13
+ |
+LL | let _ = foo as u16;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u32`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:19:13
+ |
+LL | let _ = foo as u32;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u64`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:20:13
+ |
+LL | let _ = foo as u64;
+ | ^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `foo` to `u128`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:21:13
+ |
+LL | let _ = foo as u128;
+ | ^^^^^^^^^^^ help: try: `foo as usize`
+
+error: casting function pointer `abc` to `i8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:34:13
+ |
+LL | let _ = abc as i8;
+ | ^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:35:13
+ |
+LL | let _ = abc as i16;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i32`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:36:13
+ |
+LL | let _ = abc as i32;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i64`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:37:13
+ |
+LL | let _ = abc as i64;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `i128`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:38:13
+ |
+LL | let _ = abc as i128;
+ | ^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `isize`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:39:13
+ |
+LL | let _ = abc as isize;
+ | ^^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u8`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:41:13
+ |
+LL | let _ = abc as u8;
+ | ^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u16`, which truncates the value
+ --> $DIR/fn_to_numeric_cast_32bit.rs:42:13
+ |
+LL | let _ = abc as u16;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u32`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:43:13
+ |
+LL | let _ = abc as u32;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u64`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:44:13
+ |
+LL | let _ = abc as u64;
+ | ^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `abc` to `u128`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:45:13
+ |
+LL | let _ = abc as u128;
+ | ^^^^^^^^^^^ help: try: `abc as usize`
+
+error: casting function pointer `f` to `i32`
+ --> $DIR/fn_to_numeric_cast_32bit.rs:52:5
+ |
+LL | f as i32
+ | ^^^^^^^^ help: try: `f as usize`
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs
new file mode 100644
index 000000000..467046839
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs
@@ -0,0 +1,76 @@
+#![warn(clippy::fn_to_numeric_cast_any)]
+#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
+
+fn foo() -> u8 {
+ 0
+}
+
+fn generic_foo<T>(x: T) -> T {
+ x
+}
+
+trait Trait {
+ fn static_method() -> u32 {
+ 2
+ }
+}
+
+struct Struct;
+
+impl Trait for Struct {}
+
+fn fn_pointer_to_integer() {
+ let _ = foo as i8;
+ let _ = foo as i16;
+ let _ = foo as i32;
+ let _ = foo as i64;
+ let _ = foo as i128;
+ let _ = foo as isize;
+
+ let _ = foo as u8;
+ let _ = foo as u16;
+ let _ = foo as u32;
+ let _ = foo as u64;
+ let _ = foo as u128;
+ let _ = foo as usize;
+}
+
+fn static_method_to_integer() {
+ let _ = Struct::static_method as usize;
+}
+
+fn fn_with_fn_arg(f: fn(i32) -> u32) -> usize {
+ f as usize
+}
+
+fn fn_with_generic_static_trait_method<T: Trait>() -> usize {
+ T::static_method as usize
+}
+
+fn closure_to_fn_to_integer() {
+ let clos = |x| x * 2_u32;
+
+ let _ = (clos as fn(u32) -> u32) as usize;
+}
+
+fn fn_to_raw_ptr() {
+ let _ = foo as *const ();
+}
+
+fn cast_fn_to_self() {
+ // Casting to the same function pointer type should be permitted.
+ let _ = foo as fn() -> u8;
+}
+
+fn cast_generic_to_concrete() {
+ // Casting to a more concrete function pointer type should be permitted.
+ let _ = generic_foo as fn(usize) -> usize;
+}
+
+fn cast_closure_to_fn() {
+ // Casting a closure to a function pointer should be permitted.
+ let id = |x| x;
+ let _ = id as fn(usize) -> usize;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
new file mode 100644
index 000000000..a6c4a7767
--- /dev/null
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
@@ -0,0 +1,106 @@
+error: casting function pointer `foo` to `i8`
+ --> $DIR/fn_to_numeric_cast_any.rs:23:13
+ |
+LL | let _ = foo as i8;
+ | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8`
+ |
+ = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings`
+
+error: casting function pointer `foo` to `i16`
+ --> $DIR/fn_to_numeric_cast_any.rs:24:13
+ |
+LL | let _ = foo as i16;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16`
+
+error: casting function pointer `foo` to `i32`
+ --> $DIR/fn_to_numeric_cast_any.rs:25:13
+ |
+LL | let _ = foo as i32;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32`
+
+error: casting function pointer `foo` to `i64`
+ --> $DIR/fn_to_numeric_cast_any.rs:26:13
+ |
+LL | let _ = foo as i64;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64`
+
+error: casting function pointer `foo` to `i128`
+ --> $DIR/fn_to_numeric_cast_any.rs:27:13
+ |
+LL | let _ = foo as i128;
+ | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128`
+
+error: casting function pointer `foo` to `isize`
+ --> $DIR/fn_to_numeric_cast_any.rs:28:13
+ |
+LL | let _ = foo as isize;
+ | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize`
+
+error: casting function pointer `foo` to `u8`
+ --> $DIR/fn_to_numeric_cast_any.rs:30:13
+ |
+LL | let _ = foo as u8;
+ | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8`
+
+error: casting function pointer `foo` to `u16`
+ --> $DIR/fn_to_numeric_cast_any.rs:31:13
+ |
+LL | let _ = foo as u16;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16`
+
+error: casting function pointer `foo` to `u32`
+ --> $DIR/fn_to_numeric_cast_any.rs:32:13
+ |
+LL | let _ = foo as u32;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32`
+
+error: casting function pointer `foo` to `u64`
+ --> $DIR/fn_to_numeric_cast_any.rs:33:13
+ |
+LL | let _ = foo as u64;
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64`
+
+error: casting function pointer `foo` to `u128`
+ --> $DIR/fn_to_numeric_cast_any.rs:34:13
+ |
+LL | let _ = foo as u128;
+ | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128`
+
+error: casting function pointer `foo` to `usize`
+ --> $DIR/fn_to_numeric_cast_any.rs:35:13
+ |
+LL | let _ = foo as usize;
+ | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize`
+
+error: casting function pointer `Struct::static_method` to `usize`
+ --> $DIR/fn_to_numeric_cast_any.rs:39:13
+ |
+LL | let _ = Struct::static_method as usize;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize`
+
+error: casting function pointer `f` to `usize`
+ --> $DIR/fn_to_numeric_cast_any.rs:43:5
+ |
+LL | f as usize
+ | ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize`
+
+error: casting function pointer `T::static_method` to `usize`
+ --> $DIR/fn_to_numeric_cast_any.rs:47:5
+ |
+LL | T::static_method as usize
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize`
+
+error: casting function pointer `(clos as fn(u32) -> u32)` to `usize`
+ --> $DIR/fn_to_numeric_cast_any.rs:53:13
+ |
+LL | let _ = (clos as fn(u32) -> u32) as usize;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize`
+
+error: casting function pointer `foo` to `*const ()`
+ --> $DIR/fn_to_numeric_cast_any.rs:57:13
+ |
+LL | let _ = foo as *const ();
+ | ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()`
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs
new file mode 100644
index 000000000..39a8d960a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_kv_map.rs
@@ -0,0 +1,50 @@
+#![warn(clippy::for_kv_map)]
+#![allow(clippy::used_underscore_binding)]
+
+use std::collections::*;
+use std::rc::Rc;
+
+fn main() {
+ let m: HashMap<u64, u64> = HashMap::new();
+ for (_, v) in &m {
+ let _v = v;
+ }
+
+ let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
+ for (_, v) in &*m {
+ let _v = v;
+ // Here the `*` is not actually necessary, but the test tests that we don't
+ // suggest
+ // `in *m.values()` as we used to
+ }
+
+ let mut m: HashMap<u64, u64> = HashMap::new();
+ for (_, v) in &mut m {
+ let _v = v;
+ }
+
+ let m: &mut HashMap<u64, u64> = &mut HashMap::new();
+ for (_, v) in &mut *m {
+ let _v = v;
+ }
+
+ let m: HashMap<u64, u64> = HashMap::new();
+ let rm = &m;
+ for (k, _value) in rm {
+ let _k = k;
+ }
+
+ // The following should not produce warnings.
+
+ let m: HashMap<u64, u64> = HashMap::new();
+ // No error, _value is actually used
+ for (k, _value) in &m {
+ let _ = _value;
+ let _k = k;
+ }
+
+ let m: HashMap<u64, String> = Default::default();
+ for (_, v) in m {
+ let _v = v;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr
new file mode 100644
index 000000000..e5cc7c146
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_kv_map.stderr
@@ -0,0 +1,58 @@
+error: you seem to want to iterate on a map's values
+ --> $DIR/for_kv_map.rs:9:19
+ |
+LL | for (_, v) in &m {
+ | ^^
+ |
+ = note: `-D clippy::for-kv-map` implied by `-D warnings`
+help: use the corresponding method
+ |
+LL | for v in m.values() {
+ | ~ ~~~~~~~~~~
+
+error: you seem to want to iterate on a map's values
+ --> $DIR/for_kv_map.rs:14:19
+ |
+LL | for (_, v) in &*m {
+ | ^^^
+ |
+help: use the corresponding method
+ |
+LL | for v in (*m).values() {
+ | ~ ~~~~~~~~~~~~~
+
+error: you seem to want to iterate on a map's values
+ --> $DIR/for_kv_map.rs:22:19
+ |
+LL | for (_, v) in &mut m {
+ | ^^^^^^
+ |
+help: use the corresponding method
+ |
+LL | for v in m.values_mut() {
+ | ~ ~~~~~~~~~~~~~~
+
+error: you seem to want to iterate on a map's values
+ --> $DIR/for_kv_map.rs:27:19
+ |
+LL | for (_, v) in &mut *m {
+ | ^^^^^^^
+ |
+help: use the corresponding method
+ |
+LL | for v in (*m).values_mut() {
+ | ~ ~~~~~~~~~~~~~~~~~
+
+error: you seem to want to iterate on a map's keys
+ --> $DIR/for_kv_map.rs:33:24
+ |
+LL | for (k, _value) in rm {
+ | ^^
+ |
+help: use the corresponding method
+ |
+LL | for k in rm.keys() {
+ | ~ ~~~~~~~~~
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.fixed b/src/tools/clippy/tests/ui/for_loop_fixable.fixed
new file mode 100644
index 000000000..aa69781d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loop_fixable.fixed
@@ -0,0 +1,309 @@
+// run-rustfix
+
+#![allow(dead_code, unused)]
+
+use std::collections::*;
+
+#[warn(clippy::all)]
+struct Unrelated(Vec<u8>);
+impl Unrelated {
+ fn next(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+
+ fn iter(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+}
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
+#[allow(
+ clippy::linkedlist,
+ clippy::unnecessary_mut_passed,
+ clippy::similar_names,
+ clippy::needless_borrow
+)]
+#[allow(unused_variables)]
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ // See #601
+ for i in 0..10 {
+ // no error, id_col does not exist outside the loop
+ let mut id_col = vec![0f64; 10];
+ id_col[i] = 1f64;
+ }
+
+ for _v in &vec {}
+
+ for _v in &mut vec {}
+
+ let out_vec = vec![1, 2, 3];
+ for _v in out_vec {}
+
+ for _v in &vec {} // these are fine
+ for _v in &mut vec {} // these are fine
+
+ for _v in &[1, 2, 3] {}
+
+ for _v in (&mut [1, 2, 3]).iter() {} // no error
+
+ for _v in &[0; 32] {}
+
+ for _v in [0; 33].iter() {} // no error
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _v in &ll {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _v in &vd {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _v in &bh {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _v in &hm {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _v in &bt {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _v in &hs {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _v in &bs {}
+
+ let u = Unrelated(vec![]);
+ for _v in u.next() {} // no error
+ for _v in u.iter() {} // no error
+
+ let mut out = vec![];
+ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
+
+ // Loop with explicit counter variable
+
+ // Potential false positives
+ let mut _index = 0;
+ _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ _index += 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ let mut _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index *= 2;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index = 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+
+ for _v in &vec {
+ let mut _index = 0;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index = 0;
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ for _x in 0..1 {
+ _index += 1;
+ }
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for x in &vec {
+ if *x == 1 {
+ _index += 1
+ }
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ if false {
+ _index = 0
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ {
+ let mut _x = &mut index;
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ for _v in &vec {
+ index += 1
+ }
+ println!("index: {}", index);
+
+ fn f<T>(_: &T, _: &T) -> bool {
+ unimplemented!()
+ }
+ fn g<T>(_: &mut [T], _: usize, _: usize) {
+ unimplemented!()
+ }
+ for i in 1..vec.len() {
+ if f(&vec[i - 1], &vec[i]) {
+ g(&mut vec, i - 1, i);
+ }
+ }
+
+ for mid in 1..vec.len() {
+ let (_, _) = vec.split_at(mid);
+ }
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+ let pivot = v.len() - 1;
+ let mut i = 0;
+ for j in 0..pivot {
+ if v[j] <= v[pivot] {
+ v.swap(i, j);
+ i += 1;
+ }
+ }
+ v.swap(i, pivot);
+ i
+}
+
+#[warn(clippy::needless_range_loop)]
+pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
+ // Same source and destination - don't trigger lint
+ for i in 0..dst.len() {
+ dst[d + i] = dst[s + i];
+ }
+}
+
+mod issue_2496 {
+ pub trait Handle {
+ fn new_for_index(index: usize) -> Self;
+ fn index(&self) -> usize;
+ }
+
+ pub fn test<H: Handle>() -> H {
+ for x in 0..5 {
+ let next_handle = H::new_for_index(x);
+ println!("{}", next_handle.index());
+ }
+ unimplemented!()
+ }
+}
+
+// explicit_into_iter_loop bad suggestions
+#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
+mod issue_4958 {
+ fn takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for i in iterator {
+ println!("{}", i);
+ }
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ vec![].into_iter()
+ }
+ }
+
+ fn more_tests() {
+ let t = T;
+ let r = &t;
+ let rr = &&t;
+
+ // This case is handled by `explicit_iter_loop`. No idea why.
+ for _ in &t {}
+
+ for _ in r {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ for _ in rr.into_iter() {}
+ }
+}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.rs b/src/tools/clippy/tests/ui/for_loop_fixable.rs
new file mode 100644
index 000000000..7c063d995
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loop_fixable.rs
@@ -0,0 +1,309 @@
+// run-rustfix
+
+#![allow(dead_code, unused)]
+
+use std::collections::*;
+
+#[warn(clippy::all)]
+struct Unrelated(Vec<u8>);
+impl Unrelated {
+ fn next(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+
+ fn iter(&self) -> std::slice::Iter<u8> {
+ self.0.iter()
+ }
+}
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
+#[allow(
+ clippy::linkedlist,
+ clippy::unnecessary_mut_passed,
+ clippy::similar_names,
+ clippy::needless_borrow
+)]
+#[allow(unused_variables)]
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+
+ // See #601
+ for i in 0..10 {
+ // no error, id_col does not exist outside the loop
+ let mut id_col = vec![0f64; 10];
+ id_col[i] = 1f64;
+ }
+
+ for _v in vec.iter() {}
+
+ for _v in vec.iter_mut() {}
+
+ let out_vec = vec![1, 2, 3];
+ for _v in out_vec.into_iter() {}
+
+ for _v in &vec {} // these are fine
+ for _v in &mut vec {} // these are fine
+
+ for _v in [1, 2, 3].iter() {}
+
+ for _v in (&mut [1, 2, 3]).iter() {} // no error
+
+ for _v in [0; 32].iter() {}
+
+ for _v in [0; 33].iter() {} // no error
+
+ let ll: LinkedList<()> = LinkedList::new();
+ for _v in ll.iter() {}
+
+ let vd: VecDeque<()> = VecDeque::new();
+ for _v in vd.iter() {}
+
+ let bh: BinaryHeap<()> = BinaryHeap::new();
+ for _v in bh.iter() {}
+
+ let hm: HashMap<(), ()> = HashMap::new();
+ for _v in hm.iter() {}
+
+ let bt: BTreeMap<(), ()> = BTreeMap::new();
+ for _v in bt.iter() {}
+
+ let hs: HashSet<()> = HashSet::new();
+ for _v in hs.iter() {}
+
+ let bs: BTreeSet<()> = BTreeSet::new();
+ for _v in bs.iter() {}
+
+ let u = Unrelated(vec![]);
+ for _v in u.next() {} // no error
+ for _v in u.iter() {} // no error
+
+ let mut out = vec![];
+ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
+ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
+
+ // Loop with explicit counter variable
+
+ // Potential false positives
+ let mut _index = 0;
+ _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ _index += 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ let mut _index = 1;
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index *= 2;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index = 1;
+ _index += 1
+ }
+
+ let mut _index = 0;
+
+ for _v in &vec {
+ let mut _index = 0;
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ _index += 1;
+ _index = 0;
+ }
+
+ let mut _index = 0;
+ for _v in &vec {
+ for _x in 0..1 {
+ _index += 1;
+ }
+ _index += 1
+ }
+
+ let mut _index = 0;
+ for x in &vec {
+ if *x == 1 {
+ _index += 1
+ }
+ }
+
+ let mut _index = 0;
+ if true {
+ _index = 1
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut _index = 1;
+ if false {
+ _index = 0
+ };
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ {
+ let mut _x = &mut index;
+ }
+ for _v in &vec {
+ _index += 1
+ }
+
+ let mut index = 0;
+ for _v in &vec {
+ index += 1
+ }
+ println!("index: {}", index);
+
+ fn f<T>(_: &T, _: &T) -> bool {
+ unimplemented!()
+ }
+ fn g<T>(_: &mut [T], _: usize, _: usize) {
+ unimplemented!()
+ }
+ for i in 1..vec.len() {
+ if f(&vec[i - 1], &vec[i]) {
+ g(&mut vec, i - 1, i);
+ }
+ }
+
+ for mid in 1..vec.len() {
+ let (_, _) = vec.split_at(mid);
+ }
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+ let pivot = v.len() - 1;
+ let mut i = 0;
+ for j in 0..pivot {
+ if v[j] <= v[pivot] {
+ v.swap(i, j);
+ i += 1;
+ }
+ }
+ v.swap(i, pivot);
+ i
+}
+
+#[warn(clippy::needless_range_loop)]
+pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
+ // Same source and destination - don't trigger lint
+ for i in 0..dst.len() {
+ dst[d + i] = dst[s + i];
+ }
+}
+
+mod issue_2496 {
+ pub trait Handle {
+ fn new_for_index(index: usize) -> Self;
+ fn index(&self) -> usize;
+ }
+
+ pub fn test<H: Handle>() -> H {
+ for x in 0..5 {
+ let next_handle = H::new_for_index(x);
+ println!("{}", next_handle.index());
+ }
+ unimplemented!()
+ }
+}
+
+// explicit_into_iter_loop bad suggestions
+#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
+mod issue_4958 {
+ fn takes_iterator<T>(iterator: &T)
+ where
+ for<'a> &'a T: IntoIterator<Item = &'a String>,
+ {
+ for i in iterator.into_iter() {
+ println!("{}", i);
+ }
+ }
+
+ struct T;
+ impl IntoIterator for &T {
+ type Item = ();
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+ fn into_iter(self) -> Self::IntoIter {
+ vec![].into_iter()
+ }
+ }
+
+ fn more_tests() {
+ let t = T;
+ let r = &t;
+ let rr = &&t;
+
+ // This case is handled by `explicit_iter_loop`. No idea why.
+ for _ in t.into_iter() {}
+
+ for _ in r.into_iter() {}
+
+ // No suggestion for this.
+ // We'd have to suggest `for _ in *rr {}` which is less clear.
+ for _ in rr.into_iter() {}
+ }
+}
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+ struct S;
+ impl S {
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter<T>(self) -> I<T> {
+ unimplemented!()
+ }
+ }
+
+ struct I<T>(T);
+ impl<T> Iterator for I<T> {
+ type Item = T;
+ fn next(&mut self) -> Option<Self::Item> {
+ unimplemented!()
+ }
+ }
+
+ fn f() {
+ for _ in S.into_iter::<u32>() {
+ unimplemented!()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.stderr b/src/tools/clippy/tests/ui/for_loop_fixable.stderr
new file mode 100644
index 000000000..ddfe66d67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loop_fixable.stderr
@@ -0,0 +1,96 @@
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:43:15
+ |
+LL | for _v in vec.iter() {}
+ | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
+ |
+ = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:45:15
+ |
+LL | for _v in vec.iter_mut() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:48:15
+ |
+LL | for _v in out_vec.into_iter() {}
+ | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
+ |
+ = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:53:15
+ |
+LL | for _v in [1, 2, 3].iter() {}
+ | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:57:15
+ |
+LL | for _v in [0; 32].iter() {}
+ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:62:15
+ |
+LL | for _v in ll.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:65:15
+ |
+LL | for _v in vd.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:68:15
+ |
+LL | for _v in bh.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:71:15
+ |
+LL | for _v in hm.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:74:15
+ |
+LL | for _v in bt.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:77:15
+ |
+LL | for _v in hs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:80:15
+ |
+LL | for _v in bs.iter() {}
+ | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:255:18
+ |
+LL | for i in iterator.into_iter() {
+ | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
+
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:275:18
+ |
+LL | for _ in t.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
+
+error: it is more concise to loop over containers instead of using explicit iteration methods
+ --> $DIR/for_loop_fixable.rs:277:18
+ |
+LL | for _ in r.into_iter() {}
+ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
new file mode 100644
index 000000000..efcaffce2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
@@ -0,0 +1,15 @@
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(
+ clippy::needless_range_loop,
+ clippy::explicit_iter_loop,
+ clippy::explicit_into_iter_loop,
+ clippy::iter_next_loop,
+ clippy::for_kv_map
+)]
+#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+fn main() {
+ let vec = vec![1, 2, 3, 4];
+
+ for _v in vec.iter().next() {}
+}
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
new file mode 100644
index 000000000..f769b4bdc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
@@ -0,0 +1,10 @@
+error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
+ --> $DIR/for_loop_unfixable.rs:14:15
+ |
+LL | for _v in vec.iter().next() {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iter-next-loop` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
new file mode 100644
index 000000000..3390111d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
@@ -0,0 +1,72 @@
+#![warn(clippy::for_loops_over_fallibles)]
+
+fn for_loops_over_fallibles() {
+ let option = Some(1);
+ let mut result = option.ok_or("x not found");
+ let v = vec![0, 1, 2];
+
+ // check over an `Option`
+ for x in option {
+ println!("{}", x);
+ }
+
+ // check over an `Option`
+ for x in option.iter() {
+ println!("{}", x);
+ }
+
+ // check over a `Result`
+ for x in result {
+ println!("{}", x);
+ }
+
+ // check over a `Result`
+ for x in result.iter_mut() {
+ println!("{}", x);
+ }
+
+ // check over a `Result`
+ for x in result.into_iter() {
+ println!("{}", x);
+ }
+
+ for x in option.ok_or("x not found") {
+ println!("{}", x);
+ }
+
+ // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call
+ // in the chain
+ for x in v.iter().next() {
+ println!("{}", x);
+ }
+
+ // make sure we lint when next() is not the last call in the chain
+ for x in v.iter().next().and(Some(0)) {
+ println!("{}", x);
+ }
+
+ for x in v.iter().next().ok_or("x not found") {
+ println!("{}", x);
+ }
+
+ // check for false positives
+
+ // for loop false positive
+ for x in v {
+ println!("{}", x);
+ }
+
+ // while let false positive for Option
+ while let Some(x) = option {
+ println!("{}", x);
+ break;
+ }
+
+ // while let false positive for Result
+ while let Ok(x) = result {
+ println!("{}", x);
+ break;
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
new file mode 100644
index 000000000..8c8c02224
--- /dev/null
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
@@ -0,0 +1,95 @@
+error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:9:14
+ |
+LL | for x in option {
+ | ^^^^^^
+ |
+ = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings`
+ = help: consider replacing `for x in option` with `if let Some(x) = option`
+
+error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:14:14
+ |
+LL | for x in option.iter() {
+ | ^^^^^^
+ |
+ = help: consider replacing `for x in option.iter()` with `if let Some(x) = option`
+
+error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:19:14
+ |
+LL | for x in result {
+ | ^^^^^^
+ |
+ = help: consider replacing `for x in result` with `if let Ok(x) = result`
+
+error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:24:14
+ |
+LL | for x in result.iter_mut() {
+ | ^^^^^^
+ |
+ = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result`
+
+error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:29:14
+ |
+LL | for x in result.into_iter() {
+ | ^^^^^^
+ |
+ = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result`
+
+error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:33:14
+ |
+LL | for x in option.ok_or("x not found") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
+
+error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
+ --> $DIR/for_loops_over_fallibles.rs:39:14
+ |
+LL | for x in v.iter().next() {
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `#[deny(clippy::iter_next_loop)]` on by default
+
+error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:44:14
+ |
+LL | for x in v.iter().next().and(Some(0)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
+
+error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
+ --> $DIR/for_loops_over_fallibles.rs:48:14
+ |
+LL | for x in v.iter().next().ok_or("x not found") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
+
+error: this loop never actually loops
+ --> $DIR/for_loops_over_fallibles.rs:60:5
+ |
+LL | / while let Some(x) = option {
+LL | | println!("{}", x);
+LL | | break;
+LL | | }
+ | |_____^
+ |
+ = note: `#[deny(clippy::never_loop)]` on by default
+
+error: this loop never actually loops
+ --> $DIR/for_loops_over_fallibles.rs:66:5
+ |
+LL | / while let Ok(x) = result {
+LL | | println!("{}", x);
+LL | | break;
+LL | | }
+ | |_____^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/forget_non_drop.rs b/src/tools/clippy/tests/ui/forget_non_drop.rs
new file mode 100644
index 000000000..7580cf95e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/forget_non_drop.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::forget_non_drop)]
+
+use core::mem::forget;
+
+fn forget_generic<T>(t: T) {
+ // Don't lint
+ forget(t)
+}
+
+fn main() {
+ struct Foo;
+ // Lint
+ forget(Foo);
+
+ struct Bar;
+ impl Drop for Bar {
+ fn drop(&mut self) {}
+ }
+ // Don't lint
+ forget(Bar);
+
+ struct Baz<T>(T);
+ // Lint
+ forget(Baz(Foo));
+ // Don't lint
+ forget(Baz(Bar));
+}
diff --git a/src/tools/clippy/tests/ui/forget_non_drop.stderr b/src/tools/clippy/tests/ui/forget_non_drop.stderr
new file mode 100644
index 000000000..03fb00960
--- /dev/null
+++ b/src/tools/clippy/tests/ui/forget_non_drop.stderr
@@ -0,0 +1,27 @@
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+ --> $DIR/forget_non_drop.rs:13:5
+ |
+LL | forget(Foo);
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::forget-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+ --> $DIR/forget_non_drop.rs:13:12
+ |
+LL | forget(Foo);
+ | ^^^
+
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+ --> $DIR/forget_non_drop.rs:24:5
+ |
+LL | forget(Baz(Foo));
+ | ^^^^^^^^^^^^^^^^
+ |
+note: argument has type `main::Baz<main::Foo>`
+ --> $DIR/forget_non_drop.rs:24:12
+ |
+LL | forget(Baz(Foo));
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/forget_ref.rs b/src/tools/clippy/tests/ui/forget_ref.rs
new file mode 100644
index 000000000..031b415f5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/forget_ref.rs
@@ -0,0 +1,50 @@
+#![warn(clippy::forget_ref)]
+#![allow(clippy::toplevel_ref_arg)]
+#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)]
+#![allow(clippy::borrow_deref_ref)]
+
+use std::mem::forget;
+
+struct SomeStruct;
+
+fn main() {
+ forget(&SomeStruct);
+
+ let mut owned = SomeStruct;
+ forget(&owned);
+ forget(&&owned);
+ forget(&mut owned);
+ forget(owned); //OK
+
+ let reference1 = &SomeStruct;
+ forget(&*reference1);
+
+ let reference2 = &mut SomeStruct;
+ forget(reference2);
+
+ let ref reference3 = SomeStruct;
+ forget(reference3);
+}
+
+#[allow(dead_code)]
+fn test_generic_fn_forget<T>(val: T) {
+ forget(&val);
+ forget(val); //OK
+}
+
+#[allow(dead_code)]
+fn test_similarly_named_function() {
+ fn forget<T>(_val: T) {}
+ forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name
+ std::mem::forget(&SomeStruct);
+}
+
+#[derive(Copy, Clone)]
+pub struct Error;
+fn produce_half_owl_error() -> Result<(), Error> {
+ Ok(())
+}
+
+fn produce_half_owl_ok() -> Result<bool, ()> {
+ Ok(true)
+}
diff --git a/src/tools/clippy/tests/ui/forget_ref.stderr b/src/tools/clippy/tests/ui/forget_ref.stderr
new file mode 100644
index 000000000..df5cd8cac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/forget_ref.stderr
@@ -0,0 +1,111 @@
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:11:5
+ |
+LL | forget(&SomeStruct);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::forget-ref` implied by `-D warnings`
+note: argument has type `&SomeStruct`
+ --> $DIR/forget_ref.rs:11:12
+ |
+LL | forget(&SomeStruct);
+ | ^^^^^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:14:5
+ |
+LL | forget(&owned);
+ | ^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/forget_ref.rs:14:12
+ |
+LL | forget(&owned);
+ | ^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:15:5
+ |
+LL | forget(&&owned);
+ | ^^^^^^^^^^^^^^^
+ |
+note: argument has type `&&SomeStruct`
+ --> $DIR/forget_ref.rs:15:12
+ |
+LL | forget(&&owned);
+ | ^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:16:5
+ |
+LL | forget(&mut owned);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&mut SomeStruct`
+ --> $DIR/forget_ref.rs:16:12
+ |
+LL | forget(&mut owned);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:20:5
+ |
+LL | forget(&*reference1);
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/forget_ref.rs:20:12
+ |
+LL | forget(&*reference1);
+ | ^^^^^^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:23:5
+ |
+LL | forget(reference2);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&mut SomeStruct`
+ --> $DIR/forget_ref.rs:23:12
+ |
+LL | forget(reference2);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:26:5
+ |
+LL | forget(reference3);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/forget_ref.rs:26:12
+ |
+LL | forget(reference3);
+ | ^^^^^^^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:31:5
+ |
+LL | forget(&val);
+ | ^^^^^^^^^^^^
+ |
+note: argument has type `&T`
+ --> $DIR/forget_ref.rs:31:12
+ |
+LL | forget(&val);
+ | ^^^^
+
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
+ --> $DIR/forget_ref.rs:39:5
+ |
+LL | std::mem::forget(&SomeStruct);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&SomeStruct`
+ --> $DIR/forget_ref.rs:39:22
+ |
+LL | std::mem::forget(&SomeStruct);
+ | ^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
new file mode 100644
index 000000000..6b754f3bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -0,0 +1,94 @@
+// run-rustfix
+
+#![allow(
+ unused_tuple_struct_fields,
+ clippy::print_literal,
+ clippy::redundant_clone,
+ clippy::to_string_in_format_args,
+ clippy::needless_borrow
+)]
+#![warn(clippy::useless_format)]
+
+struct Foo(pub String);
+
+macro_rules! foo {
+ ($($t:tt)*) => (Foo(format!($($t)*)))
+}
+
+fn main() {
+ "foo".to_string();
+ "{}".to_string();
+ "{} abc {}".to_string();
+ r##"foo {}
+" bar"##.to_string();
+
+ let _ = String::new();
+
+ "foo".to_string();
+ format!("{:?}", "foo"); // Don't warn about `Debug`.
+ format!("{:8}", "foo");
+ format!("{:width$}", "foo", width = 8);
+ "foo".to_string(); // Warn when the format makes no difference.
+ "foo".to_string(); // Warn when the format makes no difference.
+ format!("foo {}", "bar");
+ format!("{} bar", "foo");
+
+ let arg: String = "".to_owned();
+ arg.to_string();
+ format!("{:?}", arg); // Don't warn about debug.
+ format!("{:8}", arg);
+ format!("{:width$}", arg, width = 8);
+ arg.to_string(); // Warn when the format makes no difference.
+ arg.to_string(); // Warn when the format makes no difference.
+ format!("foo {}", arg);
+ format!("{} bar", arg);
+
+ // We don’t want to warn for non-string args; see issue #697.
+ format!("{}", 42);
+ format!("{:?}", 42);
+ format!("{:+}", 42);
+ format!("foo {}", 42);
+ format!("{} bar", 42);
+
+ // We only want to warn about `format!` itself.
+ println!("foo");
+ println!("{}", "foo");
+ println!("foo {}", "foo");
+ println!("{}", 42);
+ println!("foo {}", 42);
+
+ // A `format!` inside a macro should not trigger a warning.
+ foo!("should not warn");
+
+ // Precision on string means slicing without panicking on size.
+ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
+ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
+ format!("{:.prec$}", "foo", prec = 1);
+ format!("{:.prec$}", "foo", prec = 10);
+
+ 42.to_string();
+ let x = std::path::PathBuf::from("/bar/foo/qux");
+ x.display().to_string();
+
+ // False positive
+ let a = "foo".to_string();
+ let _ = Some(a + "bar");
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = (&*v.join("\n")).to_string();
+
+ format!("prepend {:+}", "s");
+
+ // Issue #8290
+ let x = "foo";
+ let _ = x.to_string();
+ let _ = format!("{x:?}"); // Don't lint on debug
+ let _ = x.to_string();
+
+ // Issue #9234
+ let abc = "abc";
+ let _ = abc.to_string();
+ let xx = "xx";
+ let _ = xx.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
new file mode 100644
index 000000000..ca9826b35
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -0,0 +1,96 @@
+// run-rustfix
+
+#![allow(
+ unused_tuple_struct_fields,
+ clippy::print_literal,
+ clippy::redundant_clone,
+ clippy::to_string_in_format_args,
+ clippy::needless_borrow
+)]
+#![warn(clippy::useless_format)]
+
+struct Foo(pub String);
+
+macro_rules! foo {
+ ($($t:tt)*) => (Foo(format!($($t)*)))
+}
+
+fn main() {
+ format!("foo");
+ format!("{{}}");
+ format!("{{}} abc {{}}");
+ format!(
+ r##"foo {{}}
+" bar"##
+ );
+
+ let _ = format!("");
+
+ format!("{}", "foo");
+ format!("{:?}", "foo"); // Don't warn about `Debug`.
+ format!("{:8}", "foo");
+ format!("{:width$}", "foo", width = 8);
+ format!("{:+}", "foo"); // Warn when the format makes no difference.
+ format!("{:<}", "foo"); // Warn when the format makes no difference.
+ format!("foo {}", "bar");
+ format!("{} bar", "foo");
+
+ let arg: String = "".to_owned();
+ format!("{}", arg);
+ format!("{:?}", arg); // Don't warn about debug.
+ format!("{:8}", arg);
+ format!("{:width$}", arg, width = 8);
+ format!("{:+}", arg); // Warn when the format makes no difference.
+ format!("{:<}", arg); // Warn when the format makes no difference.
+ format!("foo {}", arg);
+ format!("{} bar", arg);
+
+ // We don’t want to warn for non-string args; see issue #697.
+ format!("{}", 42);
+ format!("{:?}", 42);
+ format!("{:+}", 42);
+ format!("foo {}", 42);
+ format!("{} bar", 42);
+
+ // We only want to warn about `format!` itself.
+ println!("foo");
+ println!("{}", "foo");
+ println!("foo {}", "foo");
+ println!("{}", 42);
+ println!("foo {}", 42);
+
+ // A `format!` inside a macro should not trigger a warning.
+ foo!("should not warn");
+
+ // Precision on string means slicing without panicking on size.
+ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
+ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
+ format!("{:.prec$}", "foo", prec = 1);
+ format!("{:.prec$}", "foo", prec = 10);
+
+ format!("{}", 42.to_string());
+ let x = std::path::PathBuf::from("/bar/foo/qux");
+ format!("{}", x.display().to_string());
+
+ // False positive
+ let a = "foo".to_string();
+ let _ = Some(format!("{}", a + "bar"));
+
+ // Wrap it with braces
+ let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+ let _s: String = format!("{}", &*v.join("\n"));
+
+ format!("prepend {:+}", "s");
+
+ // Issue #8290
+ let x = "foo";
+ let _ = format!("{x}");
+ let _ = format!("{x:?}"); // Don't lint on debug
+ let _ = format!("{y}", y = x);
+
+ // Issue #9234
+ let abc = "abc";
+ let _ = format!("{abc}");
+ let xx = "xx";
+ let _ = format!("{xx}");
+}
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
new file mode 100644
index 000000000..6c35caeb0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -0,0 +1,127 @@
+error: useless use of `format!`
+ --> $DIR/format.rs:19:5
+ |
+LL | format!("foo");
+ | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+ |
+ = note: `-D clippy::useless-format` implied by `-D warnings`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:20:5
+ |
+LL | format!("{{}}");
+ | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:21:5
+ |
+LL | format!("{{}} abc {{}}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:22:5
+ |
+LL | / format!(
+LL | | r##"foo {{}}
+LL | | " bar"##
+LL | | );
+ | |_____^
+ |
+help: consider using `.to_string()`
+ |
+LL ~ r##"foo {}
+LL ~ " bar"##.to_string();
+ |
+
+error: useless use of `format!`
+ --> $DIR/format.rs:27:13
+ |
+LL | let _ = format!("");
+ | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:29:5
+ |
+LL | format!("{}", "foo");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:33:5
+ |
+LL | format!("{:+}", "foo"); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:34:5
+ |
+LL | format!("{:<}", "foo"); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:39:5
+ |
+LL | format!("{}", arg);
+ | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:43:5
+ |
+LL | format!("{:+}", arg); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:44:5
+ |
+LL | format!("{:<}", arg); // Warn when the format makes no difference.
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:71:5
+ |
+LL | format!("{}", 42.to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:73:5
+ |
+LL | format!("{}", x.display().to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:77:18
+ |
+LL | let _ = Some(format!("{}", a + "bar"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:81:22
+ |
+LL | let _s: String = format!("{}", &*v.join("/n"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:87:13
+ |
+LL | let _ = format!("{x}");
+ | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:89:13
+ |
+LL | let _ = format!("{y}", y = x);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:93:13
+ |
+LL | let _ = format!("{abc}");
+ | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:95:13
+ |
+LL | let _ = format!("{xx}");
+ | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed
new file mode 100644
index 000000000..69b5e1c72
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_args.fixed
@@ -0,0 +1,117 @@
+// run-rustfix
+
+#![allow(unreachable_code)]
+#![allow(unused_macros)]
+#![allow(unused_variables)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
+#![allow(clippy::print_literal)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+struct Somewhere;
+
+impl ToString for Somewhere {
+ fn to_string(&self) -> String {
+ String::from("somewhere")
+ }
+}
+
+struct X(u32);
+
+impl Deref for X {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+struct Y<'a>(&'a X);
+
+impl<'a> Deref for Y<'a> {
+ type Target = &'a X;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Z(u32);
+
+impl Deref for Z {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl std::fmt::Display for Z {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Z")
+ }
+}
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: something failed at {}", Location::caller().to_string());
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ Location::caller().to_string()
+ };
+}
+
+fn main() {
+ let x = &X(1);
+ let x_ref = &x;
+
+ let _ = format!("error: something failed at {}", Location::caller());
+ let _ = write!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller()
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller()
+ );
+ print!("error: something failed at {}", Location::caller());
+ println!("error: something failed at {}", Location::caller());
+ eprint!("error: something failed at {}", Location::caller());
+ eprintln!("error: something failed at {}", Location::caller());
+ let _ = format_args!("error: something failed at {}", Location::caller());
+ assert!(true, "error: something failed at {}", Location::caller());
+ assert_eq!(0, 0, "error: something failed at {}", Location::caller());
+ assert_ne!(0, 0, "error: something failed at {}", Location::caller());
+ panic!("error: something failed at {}", Location::caller());
+ println!("{}", *X(1));
+ println!("{}", ***Y(&X(1)));
+ println!("{}", Z(1));
+ println!("{}", **x);
+ println!("{}", ***x_ref);
+ // https://github.com/rust-lang/rust-clippy/issues/7903
+ println!("{foo}{bar}", foo = "foo", bar = "bar");
+ println!("{foo}{bar}", foo = "foo", bar = "bar");
+ println!("{foo}{bar}", bar = "bar", foo = "foo");
+ println!("{foo}{bar}", bar = "bar", foo = "foo");
+
+ // negative tests
+ println!("error: something failed at {}", Somewhere.to_string());
+ // The next two tests are negative because caching the string might be faster than calling `<X as
+ // Display>::fmt` twice.
+ println!("{} and again {0}", x.to_string());
+ println!("{foo}{foo}", foo = "foo".to_string());
+ my_macro!();
+ println!("error: something failed at {}", my_other_macro!());
+ // https://github.com/rust-lang/rust-clippy/issues/7903
+ println!("{foo}{foo:?}", foo = "foo".to_string());
+}
diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs
new file mode 100644
index 000000000..3a434c5bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_args.rs
@@ -0,0 +1,117 @@
+// run-rustfix
+
+#![allow(unreachable_code)]
+#![allow(unused_macros)]
+#![allow(unused_variables)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
+#![allow(clippy::print_literal)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+struct Somewhere;
+
+impl ToString for Somewhere {
+ fn to_string(&self) -> String {
+ String::from("somewhere")
+ }
+}
+
+struct X(u32);
+
+impl Deref for X {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+struct Y<'a>(&'a X);
+
+impl<'a> Deref for Y<'a> {
+ type Target = &'a X;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Z(u32);
+
+impl Deref for Z {
+ type Target = u32;
+
+ fn deref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl std::fmt::Display for Z {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Z")
+ }
+}
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: something failed at {}", Location::caller().to_string());
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ Location::caller().to_string()
+ };
+}
+
+fn main() {
+ let x = &X(1);
+ let x_ref = &x;
+
+ let _ = format!("error: something failed at {}", Location::caller().to_string());
+ let _ = write!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller().to_string()
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: something failed at {}",
+ Location::caller().to_string()
+ );
+ print!("error: something failed at {}", Location::caller().to_string());
+ println!("error: something failed at {}", Location::caller().to_string());
+ eprint!("error: something failed at {}", Location::caller().to_string());
+ eprintln!("error: something failed at {}", Location::caller().to_string());
+ let _ = format_args!("error: something failed at {}", Location::caller().to_string());
+ assert!(true, "error: something failed at {}", Location::caller().to_string());
+ assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ panic!("error: something failed at {}", Location::caller().to_string());
+ println!("{}", X(1).to_string());
+ println!("{}", Y(&X(1)).to_string());
+ println!("{}", Z(1).to_string());
+ println!("{}", x.to_string());
+ println!("{}", x_ref.to_string());
+ // https://github.com/rust-lang/rust-clippy/issues/7903
+ println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
+ println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
+ println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
+ println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
+
+ // negative tests
+ println!("error: something failed at {}", Somewhere.to_string());
+ // The next two tests are negative because caching the string might be faster than calling `<X as
+ // Display>::fmt` twice.
+ println!("{} and again {0}", x.to_string());
+ println!("{foo}{foo}", foo = "foo".to_string());
+ my_macro!();
+ println!("error: something failed at {}", my_other_macro!());
+ // https://github.com/rust-lang/rust-clippy/issues/7903
+ println!("{foo}{foo:?}", foo = "foo".to_string());
+}
diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr
new file mode 100644
index 000000000..c0cbca507
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_args.stderr
@@ -0,0 +1,130 @@
+error: `to_string` applied to a type that implements `Display` in `format!` args
+ --> $DIR/format_args.rs:76:72
+ |
+LL | let _ = format!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
+
+error: `to_string` applied to a type that implements `Display` in `write!` args
+ --> $DIR/format_args.rs:80:27
+ |
+LL | Location::caller().to_string()
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `writeln!` args
+ --> $DIR/format_args.rs:85:27
+ |
+LL | Location::caller().to_string()
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `print!` args
+ --> $DIR/format_args.rs:87:63
+ |
+LL | print!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:88:65
+ |
+LL | println!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `eprint!` args
+ --> $DIR/format_args.rs:89:64
+ |
+LL | eprint!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `eprintln!` args
+ --> $DIR/format_args.rs:90:66
+ |
+LL | eprintln!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `format_args!` args
+ --> $DIR/format_args.rs:91:77
+ |
+LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert!` args
+ --> $DIR/format_args.rs:92:70
+ |
+LL | assert!(true, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
+ --> $DIR/format_args.rs:93:73
+ |
+LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
+ --> $DIR/format_args.rs:94:73
+ |
+LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `panic!` args
+ --> $DIR/format_args.rs:95:63
+ |
+LL | panic!("error: something failed at {}", Location::caller().to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:96:20
+ |
+LL | println!("{}", X(1).to_string());
+ | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:97:20
+ |
+LL | println!("{}", Y(&X(1)).to_string());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:98:24
+ |
+LL | println!("{}", Z(1).to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:99:20
+ |
+LL | println!("{}", x.to_string());
+ | ^^^^^^^^^^^^^ help: use this: `**x`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:100:20
+ |
+LL | println!("{}", x_ref.to_string());
+ | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:102:39
+ |
+LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:103:52
+ |
+LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:104:39
+ |
+LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
+ | ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+ --> $DIR/format_args.rs:105:52
+ |
+LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
+ | ^^^^^^^^^^^^ help: remove this
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs
new file mode 100644
index 000000000..b24ddf732
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs
@@ -0,0 +1,61 @@
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::eq_op)]
+#![warn(clippy::format_in_format_args)]
+#![warn(clippy::to_string_in_format_args)]
+
+use std::io::{stdout, Error, ErrorKind, Write};
+use std::ops::Deref;
+use std::panic::Location;
+
+macro_rules! my_macro {
+ () => {
+ // here be dragons, do not enter (or lint)
+ println!("error: {}", format!("something failed at {}", Location::caller()));
+ };
+}
+
+macro_rules! my_other_macro {
+ () => {
+ format!("something failed at {}", Location::caller())
+ };
+}
+
+fn main() {
+ let error = Error::new(ErrorKind::Other, "bad thing");
+ let x = 'x';
+
+ println!("error: {}", format!("something failed at {}", Location::caller()));
+ println!("{}: {}", error, format!("something failed at {}", Location::caller()));
+ println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
+ println!("{{}}: {}", format!("something failed at {}", Location::caller()));
+ println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
+ println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
+ println!("error: {}", format!("something failed at {} {0}", Location::caller()));
+ let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
+ let _ = write!(
+ stdout(),
+ "error: {}",
+ format!("something failed at {}", Location::caller())
+ );
+ let _ = writeln!(
+ stdout(),
+ "error: {}",
+ format!("something failed at {}", Location::caller())
+ );
+ print!("error: {}", format!("something failed at {}", Location::caller()));
+ eprint!("error: {}", format!("something failed at {}", Location::caller()));
+ eprintln!("error: {}", format!("something failed at {}", Location::caller()));
+ let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
+ assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
+ assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ panic!("error: {}", format!("something failed at {}", Location::caller()));
+
+ // negative tests
+ println!("error: {}", format_args!("something failed at {}", Location::caller()));
+ println!("error: {:>70}", format!("something failed at {}", Location::caller()));
+ println!("error: {} {0}", format!("something failed at {}", Location::caller()));
+ println!("{} and again {0}", format!("hi {}", x));
+ my_macro!();
+ println!("error: {}", my_other_macro!());
+}
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.stderr b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
new file mode 100644
index 000000000..4476218ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
@@ -0,0 +1,175 @@
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:27:5
+ |
+LL | println!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::format-in-format-args` implied by `-D warnings`
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:28:5
+ |
+LL | println!("{}: {}", error, format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:29:5
+ |
+LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:30:5
+ |
+LL | println!("{{}}: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:31:5
+ |
+LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:32:5
+ |
+LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `println!` args
+ --> $DIR/format_args_unfixable.rs:33:5
+ |
+LL | println!("error: {}", format!("something failed at {} {0}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `println!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `format!` args
+ --> $DIR/format_args_unfixable.rs:34:13
+ |
+LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `format!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `write!` args
+ --> $DIR/format_args_unfixable.rs:35:13
+ |
+LL | let _ = write!(
+ | _____________^
+LL | | stdout(),
+LL | | "error: {}",
+LL | | format!("something failed at {}", Location::caller())
+LL | | );
+ | |_____^
+ |
+ = help: combine the `format!(..)` arguments with the outer `write!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `writeln!` args
+ --> $DIR/format_args_unfixable.rs:40:13
+ |
+LL | let _ = writeln!(
+ | _____________^
+LL | | stdout(),
+LL | | "error: {}",
+LL | | format!("something failed at {}", Location::caller())
+LL | | );
+ | |_____^
+ |
+ = help: combine the `format!(..)` arguments with the outer `writeln!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `print!` args
+ --> $DIR/format_args_unfixable.rs:45:5
+ |
+LL | print!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `print!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `eprint!` args
+ --> $DIR/format_args_unfixable.rs:46:5
+ |
+LL | eprint!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `eprint!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `eprintln!` args
+ --> $DIR/format_args_unfixable.rs:47:5
+ |
+LL | eprintln!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `eprintln!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `format_args!` args
+ --> $DIR/format_args_unfixable.rs:48:13
+ |
+LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `format_args!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `assert!` args
+ --> $DIR/format_args_unfixable.rs:49:5
+ |
+LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `assert!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `assert_eq!` args
+ --> $DIR/format_args_unfixable.rs:50:5
+ |
+LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `assert_eq!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `assert_ne!` args
+ --> $DIR/format_args_unfixable.rs:51:5
+ |
+LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `assert_ne!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `panic!` args
+ --> $DIR/format_args_unfixable.rs:52:5
+ |
+LL | panic!("error: {}", format!("something failed at {}", Location::caller()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: combine the `format!(..)` arguments with the outer `panic!(..)` call
+ = help: or consider changing `format!` to `format_args!`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/format_push_string.rs b/src/tools/clippy/tests/ui/format_push_string.rs
new file mode 100644
index 000000000..4db13d650
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_push_string.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::format_push_string)]
+
+fn main() {
+ let mut string = String::new();
+ string += &format!("{:?}", 1234);
+ string.push_str(&format!("{:?}", 5678));
+}
diff --git a/src/tools/clippy/tests/ui/format_push_string.stderr b/src/tools/clippy/tests/ui/format_push_string.stderr
new file mode 100644
index 000000000..953784bcc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/format_push_string.stderr
@@ -0,0 +1,19 @@
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:5:5
+ |
+LL | string += &format!("{:?}", 1234);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::format-push-string` implied by `-D warnings`
+ = help: consider using `write!` to avoid the extra allocation
+
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:6:5
+ |
+LL | string.push_str(&format!("{:?}", 5678));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `write!` to avoid the extra allocation
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs
new file mode 100644
index 000000000..471a8e0de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/formatting.rs
@@ -0,0 +1,73 @@
+#![warn(clippy::all)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#![allow(clippy::if_same_then_else)]
+#![allow(clippy::deref_addrof)]
+#![allow(clippy::nonminimal_bool)]
+
+fn foo() -> bool {
+ true
+}
+
+#[rustfmt::skip]
+fn main() {
+ // weird op_eq formatting:
+ let mut a = 42;
+ a =- 35;
+ a =* &191;
+
+ let mut b = true;
+ b =! false;
+
+ // those are ok:
+ a = -35;
+ a = *&191;
+ b = !false;
+
+ // possible missing comma in an array
+ let _ = &[
+ -1, -2, -3 // <= no comma here
+ -4, -5, -6
+ ];
+ let _ = &[
+ -1, -2, -3 // <= no comma here
+ *4, -5, -6
+ ];
+
+ // those are ok:
+ let _ = &[
+ -1, -2, -3,
+ -4, -5, -6
+ ];
+ let _ = &[
+ -1, -2, -3,
+ -4, -5, -6,
+ ];
+ let _ = &[
+ 1 + 2, 3 +
+ 4, 5 + 6,
+ ];
+
+ // don't lint for bin op without unary equiv
+ // issue 3244
+ vec![
+ 1
+ / 2,
+ ];
+ // issue 3396
+ vec![
+ true
+ | false,
+ ];
+
+ // don't lint if the indentation suggests not to
+ let _ = &[
+ 1 + 2, 3
+ - 4, 5
+ ];
+ // lint if it doesn't
+ let _ = &[
+ -1
+ -4,
+ ];
+}
diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr
new file mode 100644
index 000000000..9272cd604
--- /dev/null
+++ b/src/tools/clippy/tests/ui/formatting.stderr
@@ -0,0 +1,52 @@
+error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
+ --> $DIR/formatting.rs:16:6
+ |
+LL | a =- 35;
+ | ^^^^
+ |
+ = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
+ = note: to remove this lint, use either `-=` or `= -`
+
+error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
+ --> $DIR/formatting.rs:17:6
+ |
+LL | a =* &191;
+ | ^^^^
+ |
+ = note: to remove this lint, use either `*=` or `= *`
+
+error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
+ --> $DIR/formatting.rs:20:6
+ |
+LL | b =! false;
+ | ^^^^
+ |
+ = note: to remove this lint, use either `!=` or `= !`
+
+error: possibly missing a comma here
+ --> $DIR/formatting.rs:29:19
+ |
+LL | -1, -2, -3 // <= no comma here
+ | ^
+ |
+ = note: `-D clippy::possible-missing-comma` implied by `-D warnings`
+ = note: to remove this lint, add a comma or write the expr in a single line
+
+error: possibly missing a comma here
+ --> $DIR/formatting.rs:33:19
+ |
+LL | -1, -2, -3 // <= no comma here
+ | ^
+ |
+ = note: to remove this lint, add a comma or write the expr in a single line
+
+error: possibly missing a comma here
+ --> $DIR/formatting.rs:70:11
+ |
+LL | -1
+ | ^
+ |
+ = note: to remove this lint, add a comma or write the expr in a single line
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed
new file mode 100644
index 000000000..48f809331
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed
@@ -0,0 +1,61 @@
+// run-rustfix
+
+#![warn(clippy::from_iter_instead_of_collect)]
+#![allow(unused_imports, unused_tuple_struct_fields)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
+
+struct Foo(Vec<bool>);
+
+impl FromIterator<bool> for Foo {
+ fn from_iter<T: IntoIterator<Item = bool>>(_: T) -> Self {
+ todo!()
+ }
+}
+
+impl<'a> FromIterator<&'a bool> for Foo {
+ fn from_iter<T: IntoIterator<Item = &'a bool>>(iter: T) -> Self {
+ iter.into_iter().copied().collect::<Self>()
+ }
+}
+
+fn main() {
+ let iter_expr = std::iter::repeat(5).take(5);
+ let _ = iter_expr.collect::<Vec<_>>();
+
+ let _ = vec![5, 5, 5, 5].iter().enumerate().collect::<HashMap<usize, &i8>>();
+
+ Vec::from_iter(vec![42u32]);
+
+ let a = vec![0, 1, 2];
+ assert_eq!(a, (0..3).collect::<Vec<_>>());
+ assert_eq!(a, (0..3).collect::<Vec<i32>>());
+
+ let mut b = (0..3).collect::<VecDeque<_>>();
+ b.push_back(4);
+
+ let mut b = (0..3).collect::<VecDeque<i32>>();
+ b.push_back(4);
+
+ {
+ use std::collections;
+ let mut b = (0..3).collect::<collections::VecDeque<i32>>();
+ b.push_back(4);
+ }
+
+ let values = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')];
+ let bm = values.iter().cloned().collect::<BTreeMap<_, _>>();
+ let mut bar = bm.range(0..2).collect::<BTreeMap<_, _>>();
+ bar.insert(&4, &'e');
+
+ let mut bts = (0..3).collect::<BTreeSet<_>>();
+ bts.insert(2);
+ {
+ use std::collections;
+ let _ = (0..3).collect::<collections::BTreeSet<_>>();
+ let _ = (0..3).collect::<collections::BTreeSet<u32>>();
+ }
+
+ for _i in [1, 2, 3].iter().collect::<Vec<_>>() {}
+ for _i in [1, 2, 3].iter().collect::<Vec<&i32>>() {}
+}
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs
new file mode 100644
index 000000000..ebe0ad278
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs
@@ -0,0 +1,61 @@
+// run-rustfix
+
+#![warn(clippy::from_iter_instead_of_collect)]
+#![allow(unused_imports, unused_tuple_struct_fields)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
+
+struct Foo(Vec<bool>);
+
+impl FromIterator<bool> for Foo {
+ fn from_iter<T: IntoIterator<Item = bool>>(_: T) -> Self {
+ todo!()
+ }
+}
+
+impl<'a> FromIterator<&'a bool> for Foo {
+ fn from_iter<T: IntoIterator<Item = &'a bool>>(iter: T) -> Self {
+ <Self as FromIterator<bool>>::from_iter(iter.into_iter().copied())
+ }
+}
+
+fn main() {
+ let iter_expr = std::iter::repeat(5).take(5);
+ let _ = Vec::from_iter(iter_expr);
+
+ let _ = HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
+
+ Vec::from_iter(vec![42u32]);
+
+ let a = vec![0, 1, 2];
+ assert_eq!(a, Vec::from_iter(0..3));
+ assert_eq!(a, Vec::<i32>::from_iter(0..3));
+
+ let mut b = VecDeque::from_iter(0..3);
+ b.push_back(4);
+
+ let mut b = VecDeque::<i32>::from_iter(0..3);
+ b.push_back(4);
+
+ {
+ use std::collections;
+ let mut b = collections::VecDeque::<i32>::from_iter(0..3);
+ b.push_back(4);
+ }
+
+ let values = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')];
+ let bm = BTreeMap::from_iter(values.iter().cloned());
+ let mut bar = BTreeMap::from_iter(bm.range(0..2));
+ bar.insert(&4, &'e');
+
+ let mut bts = BTreeSet::from_iter(0..3);
+ bts.insert(2);
+ {
+ use std::collections;
+ let _ = collections::BTreeSet::from_iter(0..3);
+ let _ = collections::BTreeSet::<u32>::from_iter(0..3);
+ }
+
+ for _i in Vec::from_iter([1, 2, 3].iter()) {}
+ for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {}
+}
diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr
new file mode 100644
index 000000000..8aa3c3c01
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr
@@ -0,0 +1,94 @@
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:18:9
+ |
+LL | <Self as FromIterator<bool>>::from_iter(iter.into_iter().copied())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::<Self>()`
+ |
+ = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:24:13
+ |
+LL | let _ = Vec::from_iter(iter_expr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::<Vec<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:26:13
+ |
+LL | let _ = HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::<HashMap<usize, &i8>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:31:19
+ |
+LL | assert_eq!(a, Vec::from_iter(0..3));
+ | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:32:19
+ |
+LL | assert_eq!(a, Vec::<i32>::from_iter(0..3));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<i32>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:34:17
+ |
+LL | let mut b = VecDeque::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:37:17
+ |
+LL | let mut b = VecDeque::<i32>::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<i32>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:42:21
+ |
+LL | let mut b = collections::VecDeque::<i32>::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::VecDeque<i32>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:47:14
+ |
+LL | let bm = BTreeMap::from_iter(values.iter().cloned());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::<BTreeMap<_, _>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:48:19
+ |
+LL | let mut bar = BTreeMap::from_iter(bm.range(0..2));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::<BTreeMap<_, _>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:51:19
+ |
+LL | let mut bts = BTreeSet::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<BTreeSet<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:55:17
+ |
+LL | let _ = collections::BTreeSet::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:56:17
+ |
+LL | let _ = collections::BTreeSet::<u32>::from_iter(0..3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<u32>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:59:15
+ |
+LL | for _i in Vec::from_iter([1, 2, 3].iter()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<_>>()`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:60:15
+ |
+LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<&i32>>()`
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs
new file mode 100644
index 000000000..292d0924f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_over_into.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::from_over_into)]
+
+// this should throw an error
+struct StringWrapper(String);
+
+impl Into<StringWrapper> for String {
+ fn into(self) -> StringWrapper {
+ StringWrapper(self)
+ }
+}
+
+// this is fine
+struct A(String);
+
+impl From<String> for A {
+ fn from(s: String) -> A {
+ A(s)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr
new file mode 100644
index 000000000..2951e6bda
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_over_into.stderr
@@ -0,0 +1,11 @@
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+ --> $DIR/from_over_into.rs:6:1
+ |
+LL | impl Into<StringWrapper> for String {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::from-over-into` implied by `-D warnings`
+ = help: consider to implement `From<std::string::String>` instead
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs
new file mode 100644
index 000000000..2f2ea0484
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs
@@ -0,0 +1,52 @@
+#![warn(clippy::from_str_radix_10)]
+
+mod some_mod {
+ // fake function that shouldn't trigger the lint
+ pub fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
+ unimplemented!()
+ }
+}
+
+// fake function that shouldn't trigger the lint
+fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
+ unimplemented!()
+}
+
+// to test parenthesis addition
+struct Test;
+
+impl std::ops::Add<Test> for Test {
+ type Output = &'static str;
+
+ fn add(self, _: Self) -> Self::Output {
+ "304"
+ }
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ // all of these should trigger the lint
+ u32::from_str_radix("30", 10)?;
+ i64::from_str_radix("24", 10)?;
+ isize::from_str_radix("100", 10)?;
+ u8::from_str_radix("7", 10)?;
+ u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
+ i128::from_str_radix(Test + Test, 10)?;
+
+ let string = "300";
+ i32::from_str_radix(string, 10)?;
+
+ let stringier = "400".to_string();
+ i32::from_str_radix(&stringier, 10)?;
+
+ // none of these should trigger the lint
+ u16::from_str_radix("20", 3)?;
+ i32::from_str_radix("45", 12)?;
+ usize::from_str_radix("10", 16)?;
+ i128::from_str_radix("10", 13)?;
+ some_mod::from_str_radix("50", 10)?;
+ some_mod::from_str_radix("50", 6)?;
+ from_str_radix("50", 10)?;
+ from_str_radix("50", 6)?;
+
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
new file mode 100644
index 000000000..da5c16f8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
@@ -0,0 +1,52 @@
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:28:5
+ |
+LL | u32::from_str_radix("30", 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
+ |
+ = note: `-D clippy::from-str-radix-10` implied by `-D warnings`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:29:5
+ |
+LL | i64::from_str_radix("24", 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:30:5
+ |
+LL | isize::from_str_radix("100", 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:31:5
+ |
+LL | u8::from_str_radix("7", 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:32:5
+ |
+LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:33:5
+ |
+LL | i128::from_str_radix(Test + Test, 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:36:5
+ |
+LL | i32::from_str_radix(string, 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+ --> $DIR/from_str_radix_10.rs:39:5
+ |
+LL | i32::from_str_radix(&stringier, 10)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/functions.rs b/src/tools/clippy/tests/ui/functions.rs
new file mode 100644
index 000000000..5521870ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/functions.rs
@@ -0,0 +1,112 @@
+#![warn(clippy::all)]
+#![allow(dead_code)]
+#![allow(unused_unsafe, clippy::missing_safety_doc)]
+
+// TOO_MANY_ARGUMENTS
+fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
+
+fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
+
+#[rustfmt::skip]
+fn bad_multiline(
+ one: u32,
+ two: u32,
+ three: &str,
+ four: bool,
+ five: f32,
+ six: f32,
+ seven: bool,
+ eight: ()
+) {
+ let _one = one;
+ let _two = two;
+ let _three = three;
+ let _four = four;
+ let _five = five;
+ let _six = six;
+ let _seven = seven;
+}
+
+// don't lint extern fns
+extern "C" fn extern_fn(
+ _one: u32,
+ _two: u32,
+ _three: *const u8,
+ _four: bool,
+ _five: f32,
+ _six: f32,
+ _seven: bool,
+ _eight: *const std::ffi::c_void,
+) {
+}
+
+pub trait Foo {
+ fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool);
+ fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
+
+ fn ptr(p: *const u8);
+}
+
+pub struct Bar;
+
+impl Bar {
+ fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
+ fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
+}
+
+// ok, we don’t want to warn implementations
+impl Foo for Bar {
+ fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
+ fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
+
+ fn ptr(p: *const u8) {
+ println!("{}", unsafe { *p });
+ println!("{:?}", unsafe { p.as_ref() });
+ unsafe { std::ptr::read(p) };
+ }
+}
+
+// NOT_UNSAFE_PTR_ARG_DEREF
+
+fn private(p: *const u8) {
+ println!("{}", unsafe { *p });
+}
+
+pub fn public(p: *const u8) {
+ println!("{}", unsafe { *p });
+ println!("{:?}", unsafe { p.as_ref() });
+ unsafe { std::ptr::read(p) };
+}
+
+type Alias = *const u8;
+
+pub fn type_alias(p: Alias) {
+ println!("{}", unsafe { *p });
+ println!("{:?}", unsafe { p.as_ref() });
+ unsafe { std::ptr::read(p) };
+}
+
+impl Bar {
+ fn private(self, p: *const u8) {
+ println!("{}", unsafe { *p });
+ }
+
+ pub fn public(self, p: *const u8) {
+ println!("{}", unsafe { *p });
+ println!("{:?}", unsafe { p.as_ref() });
+ unsafe { std::ptr::read(p) };
+ }
+
+ pub fn public_ok(self, p: *const u8) {
+ if !p.is_null() {
+ println!("{:p}", p);
+ }
+ }
+
+ pub unsafe fn public_unsafe(self, p: *const u8) {
+ println!("{}", unsafe { *p });
+ println!("{:?}", unsafe { p.as_ref() });
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr
new file mode 100644
index 000000000..8ebd4997f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/functions.stderr
@@ -0,0 +1,108 @@
+error: this function has too many arguments (8/7)
+ --> $DIR/functions.rs:8:1
+ |
+LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::too-many-arguments` implied by `-D warnings`
+
+error: this function has too many arguments (8/7)
+ --> $DIR/functions.rs:11:1
+ |
+LL | / fn bad_multiline(
+LL | | one: u32,
+LL | | two: u32,
+LL | | three: &str,
+... |
+LL | | eight: ()
+LL | | ) {
+ | |__^
+
+error: this function has too many arguments (8/7)
+ --> $DIR/functions.rs:45:5
+ |
+LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this function has too many arguments (8/7)
+ --> $DIR/functions.rs:54:5
+ |
+LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:63:34
+ |
+LL | println!("{}", unsafe { *p });
+ | ^
+ |
+ = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings`
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:64:35
+ |
+LL | println!("{:?}", unsafe { p.as_ref() });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:65:33
+ |
+LL | unsafe { std::ptr::read(p) };
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:76:30
+ |
+LL | println!("{}", unsafe { *p });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:77:31
+ |
+LL | println!("{:?}", unsafe { p.as_ref() });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:78:29
+ |
+LL | unsafe { std::ptr::read(p) };
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:84:30
+ |
+LL | println!("{}", unsafe { *p });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:85:31
+ |
+LL | println!("{:?}", unsafe { p.as_ref() });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:86:29
+ |
+LL | unsafe { std::ptr::read(p) };
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:95:34
+ |
+LL | println!("{}", unsafe { *p });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:96:35
+ |
+LL | println!("{:?}", unsafe { p.as_ref() });
+ | ^
+
+error: this public function might dereference a raw pointer but is not marked `unsafe`
+ --> $DIR/functions.rs:97:33
+ |
+LL | unsafe { std::ptr::read(p) };
+ | ^
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/functions_maxlines.rs b/src/tools/clippy/tests/ui/functions_maxlines.rs
new file mode 100644
index 000000000..5e1ee55e0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/functions_maxlines.rs
@@ -0,0 +1,163 @@
+#![warn(clippy::too_many_lines)]
+
+fn good_lines() {
+ /* println!("This is good."); */
+ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* */ // println!("This is good.");
+ /* println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good."); */
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+ println!("This is good.");
+}
+
+fn bad_lines() {
+ println!("Dont get confused by braces: {{}}");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+ println!("This is bad.");
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/functions_maxlines.stderr b/src/tools/clippy/tests/ui/functions_maxlines.stderr
new file mode 100644
index 000000000..dc6c8ba2f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/functions_maxlines.stderr
@@ -0,0 +1,16 @@
+error: this function has too many lines (102/100)
+ --> $DIR/functions_maxlines.rs:58:1
+ |
+LL | / fn bad_lines() {
+LL | | println!("Dont get confused by braces: {{}}");
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+... |
+LL | | println!("This is bad.");
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::too-many-lines` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/future_not_send.rs b/src/tools/clippy/tests/ui/future_not_send.rs
new file mode 100644
index 000000000..858036692
--- /dev/null
+++ b/src/tools/clippy/tests/ui/future_not_send.rs
@@ -0,0 +1,79 @@
+#![warn(clippy::future_not_send)]
+
+use std::cell::Cell;
+use std::rc::Rc;
+use std::sync::Arc;
+
+async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ async { true }.await
+}
+
+pub async fn public_future(rc: Rc<[u8]>) {
+ async { true }.await;
+}
+
+pub async fn public_send(arc: Arc<[u8]>) -> bool {
+ async { false }.await
+}
+
+async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ true
+}
+
+pub async fn public_future2(rc: Rc<[u8]>) {}
+
+pub async fn public_send2(arc: Arc<[u8]>) -> bool {
+ false
+}
+
+struct Dummy {
+ rc: Rc<[u8]>,
+}
+
+impl Dummy {
+ async fn private_future(&self) -> usize {
+ async { true }.await;
+ self.rc.len()
+ }
+
+ pub async fn public_future(&self) {
+ self.private_future().await;
+ }
+
+ #[allow(clippy::manual_async_fn)]
+ pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
+ async { false }
+ }
+}
+
+async fn generic_future<T>(t: T) -> T
+where
+ T: Send,
+{
+ let rt = &t;
+ async { true }.await;
+ t
+}
+
+async fn generic_future_send<T>(t: T)
+where
+ T: Send,
+{
+ async { true }.await;
+}
+
+async fn unclear_future<T>(t: T) {}
+
+fn main() {
+ let rc = Rc::new([1, 2, 3]);
+ private_future(rc.clone(), &Cell::new(42));
+ public_future(rc.clone());
+ let arc = Arc::new([4, 5, 6]);
+ public_send(arc);
+ generic_future(42);
+ generic_future_send(42);
+
+ let dummy = Dummy { rc };
+ dummy.public_future();
+ dummy.public_send();
+}
diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr
new file mode 100644
index 000000000..a9f2ad36d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/future_not_send.stderr
@@ -0,0 +1,145 @@
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:7:62
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ future returned by `private_future` is not `Send`
+ |
+ = note: `-D clippy::future-not-send` implied by `-D warnings`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:8:19
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:8:19
+ |
+LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ---- has type `&std::cell::Cell<usize>` which is not `Send`
+LL | async { true }.await
+ | ^^^^^^ await occurs here, with `cell` maybe used later
+LL | }
+ | - `cell` is later dropped here
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:11:42
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:12:19
+ |
+LL | pub async fn public_future(rc: Rc<[u8]>) {
+ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^ await occurs here, with `rc` maybe used later
+LL | }
+ | - `rc` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:19:63
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ future returned by `private_future2` is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/future_not_send.rs:19:26
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ --> $DIR/future_not_send.rs:19:40
+ |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+ | ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`, because `std::cell::Cell<usize>` is not `Sync`
+ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:23:43
+ |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+ | ^ future returned by `public_future2` is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/future_not_send.rs:23:29
+ |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+ | ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:34:39
+ |
+LL | async fn private_future(&self) -> usize {
+ | ^^^^^ future returned by `private_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:35:23
+ |
+LL | async fn private_future(&self) -> usize {
+ | ----- has type `&Dummy` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^ await occurs here, with `&self` maybe used later
+LL | self.rc.len()
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:39:39
+ |
+LL | pub async fn public_future(&self) {
+ | ^ future returned by `public_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:40:30
+ |
+LL | pub async fn public_future(&self) {
+ | ----- has type `&Dummy` which is not `Send`
+LL | self.private_future().await;
+ | ^^^^^^ await occurs here, with `&self` maybe used later
+LL | }
+ | - `&self` is later dropped here
+ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:49:37
+ |
+LL | async fn generic_future<T>(t: T) -> T
+ | ^ future returned by `generic_future` is not `Send`
+ |
+note: future is not `Send` as this value is used across an await
+ --> $DIR/future_not_send.rs:54:19
+ |
+LL | let rt = &t;
+ | -- has type `&T` which is not `Send`
+LL | async { true }.await;
+ | ^^^^^^ await occurs here, with `rt` maybe used later
+LL | t
+LL | }
+ | - `rt` is later dropped here
+ = note: `T` doesn't implement `std::marker::Sync`
+
+error: future cannot be sent between threads safely
+ --> $DIR/future_not_send.rs:65:34
+ |
+LL | async fn unclear_future<T>(t: T) {}
+ | ^ future returned by `unclear_future` is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/future_not_send.rs:65:28
+ |
+LL | async fn unclear_future<T>(t: T) {}
+ | ^ has type `T` which is not `Send`
+ = note: `T` doesn't implement `std::marker::Send`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/get_first.fixed b/src/tools/clippy/tests/ui/get_first.fixed
new file mode 100644
index 000000000..def58afa4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_first.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::get_first)]
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+struct Bar {
+ arr: [u32; 3],
+}
+
+impl Bar {
+ fn get(&self, pos: usize) -> Option<&u32> {
+ self.arr.get(pos)
+ }
+}
+
+fn main() {
+ let x = vec![2, 3, 5];
+ let _ = x.first(); // Use x.first()
+ let _ = x.get(1);
+ let _ = x[0];
+
+ let y = [2, 3, 5];
+ let _ = y.first(); // Use y.first()
+ let _ = y.get(1);
+ let _ = y[0];
+
+ let z = &[2, 3, 5];
+ let _ = z.first(); // Use z.first()
+ let _ = z.get(1);
+ let _ = z[0];
+
+ let vecdeque: VecDeque<_> = x.iter().cloned().collect();
+ let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
+ let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
+ let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
+ let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
+ let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
+
+ let bar = Bar { arr: [0, 1, 2] };
+ let _ = bar.get(0); // Do not lint, because Bar is struct.
+}
diff --git a/src/tools/clippy/tests/ui/get_first.rs b/src/tools/clippy/tests/ui/get_first.rs
new file mode 100644
index 000000000..85a381854
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_first.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+#![warn(clippy::get_first)]
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+struct Bar {
+ arr: [u32; 3],
+}
+
+impl Bar {
+ fn get(&self, pos: usize) -> Option<&u32> {
+ self.arr.get(pos)
+ }
+}
+
+fn main() {
+ let x = vec![2, 3, 5];
+ let _ = x.get(0); // Use x.first()
+ let _ = x.get(1);
+ let _ = x[0];
+
+ let y = [2, 3, 5];
+ let _ = y.get(0); // Use y.first()
+ let _ = y.get(1);
+ let _ = y[0];
+
+ let z = &[2, 3, 5];
+ let _ = z.get(0); // Use z.first()
+ let _ = z.get(1);
+ let _ = z[0];
+
+ let vecdeque: VecDeque<_> = x.iter().cloned().collect();
+ let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
+ let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
+ let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
+ let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
+ let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
+
+ let bar = Bar { arr: [0, 1, 2] };
+ let _ = bar.get(0); // Do not lint, because Bar is struct.
+}
diff --git a/src/tools/clippy/tests/ui/get_first.stderr b/src/tools/clippy/tests/ui/get_first.stderr
new file mode 100644
index 000000000..466beff9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_first.stderr
@@ -0,0 +1,22 @@
+error: accessing first element with `x.get(0)`
+ --> $DIR/get_first.rs:19:13
+ |
+LL | let _ = x.get(0); // Use x.first()
+ | ^^^^^^^^ help: try: `x.first()`
+ |
+ = note: `-D clippy::get-first` implied by `-D warnings`
+
+error: accessing first element with `y.get(0)`
+ --> $DIR/get_first.rs:24:13
+ |
+LL | let _ = y.get(0); // Use y.first()
+ | ^^^^^^^^ help: try: `y.first()`
+
+error: accessing first element with `z.get(0)`
+ --> $DIR/get_first.rs:29:13
+ |
+LL | let _ = z.get(0); // Use z.first()
+ | ^^^^^^^^ help: try: `z.first()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/get_last_with_len.fixed b/src/tools/clippy/tests/ui/get_last_with_len.fixed
new file mode 100644
index 000000000..1e90b3768
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_last_with_len.fixed
@@ -0,0 +1,49 @@
+// run-rustfix
+
+#![warn(clippy::get_last_with_len)]
+#![allow(unused)]
+
+use std::collections::VecDeque;
+
+fn dont_use_last() {
+ let x = vec![2, 3, 5];
+ let _ = x.last();
+}
+
+fn indexing_two_from_end() {
+ let x = vec![2, 3, 5];
+ let _ = x.get(x.len() - 2);
+}
+
+fn index_into_last() {
+ let x = vec![2, 3, 5];
+ let _ = x[x.len() - 1];
+}
+
+fn use_last_with_different_vec_length() {
+ let x = vec![2, 3, 5];
+ let y = vec!['a', 'b', 'c'];
+ let _ = x.get(y.len() - 1);
+}
+
+struct S {
+ field: Vec<usize>,
+}
+
+fn in_field(s: &S) {
+ let _ = s.field.last();
+}
+
+fn main() {
+ let slice = &[1, 2, 3];
+ let _ = slice.last();
+
+ let array = [4, 5, 6];
+ let _ = array.last();
+
+ let deq = VecDeque::from([7, 8, 9]);
+ let _ = deq.back();
+
+ let nested = [[1]];
+ let _ = nested[0].last();
+}
diff --git a/src/tools/clippy/tests/ui/get_last_with_len.rs b/src/tools/clippy/tests/ui/get_last_with_len.rs
new file mode 100644
index 000000000..d63a731bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_last_with_len.rs
@@ -0,0 +1,49 @@
+// run-rustfix
+
+#![warn(clippy::get_last_with_len)]
+#![allow(unused)]
+
+use std::collections::VecDeque;
+
+fn dont_use_last() {
+ let x = vec![2, 3, 5];
+ let _ = x.get(x.len() - 1);
+}
+
+fn indexing_two_from_end() {
+ let x = vec![2, 3, 5];
+ let _ = x.get(x.len() - 2);
+}
+
+fn index_into_last() {
+ let x = vec![2, 3, 5];
+ let _ = x[x.len() - 1];
+}
+
+fn use_last_with_different_vec_length() {
+ let x = vec![2, 3, 5];
+ let y = vec!['a', 'b', 'c'];
+ let _ = x.get(y.len() - 1);
+}
+
+struct S {
+ field: Vec<usize>,
+}
+
+fn in_field(s: &S) {
+ let _ = s.field.get(s.field.len() - 1);
+}
+
+fn main() {
+ let slice = &[1, 2, 3];
+ let _ = slice.get(slice.len() - 1);
+
+ let array = [4, 5, 6];
+ let _ = array.get(array.len() - 1);
+
+ let deq = VecDeque::from([7, 8, 9]);
+ let _ = deq.get(deq.len() - 1);
+
+ let nested = [[1]];
+ let _ = nested[0].get(nested[0].len() - 1);
+}
diff --git a/src/tools/clippy/tests/ui/get_last_with_len.stderr b/src/tools/clippy/tests/ui/get_last_with_len.stderr
new file mode 100644
index 000000000..ac8dd6c2e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_last_with_len.stderr
@@ -0,0 +1,40 @@
+error: accessing last element with `x.get(x.len() - 1)`
+ --> $DIR/get_last_with_len.rs:10:13
+ |
+LL | let _ = x.get(x.len() - 1);
+ | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()`
+ |
+ = note: `-D clippy::get-last-with-len` implied by `-D warnings`
+
+error: accessing last element with `s.field.get(s.field.len() - 1)`
+ --> $DIR/get_last_with_len.rs:34:13
+ |
+LL | let _ = s.field.get(s.field.len() - 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.field.last()`
+
+error: accessing last element with `slice.get(slice.len() - 1)`
+ --> $DIR/get_last_with_len.rs:39:13
+ |
+LL | let _ = slice.get(slice.len() - 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.last()`
+
+error: accessing last element with `array.get(array.len() - 1)`
+ --> $DIR/get_last_with_len.rs:42:13
+ |
+LL | let _ = array.get(array.len() - 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array.last()`
+
+error: accessing last element with `deq.get(deq.len() - 1)`
+ --> $DIR/get_last_with_len.rs:45:13
+ |
+LL | let _ = deq.get(deq.len() - 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `deq.back()`
+
+error: accessing last element with `nested[0].get(nested[0].len() - 1)`
+ --> $DIR/get_last_with_len.rs:48:13
+ |
+LL | let _ = nested[0].get(nested[0].len() - 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `nested[0].last()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed
new file mode 100644
index 000000000..5827fc7d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_unwrap.fixed
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
+#![warn(clippy::unwrap_used)]
+#![deny(clippy::get_unwrap)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+struct GetFalsePositive {
+ arr: [u32; 3],
+}
+
+impl GetFalsePositive {
+ fn get(&self, pos: usize) -> Option<&u32> {
+ self.arr.get(pos)
+ }
+ fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
+ self.arr.get_mut(pos)
+ }
+}
+
+fn main() {
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut some_slice = &mut [0, 1, 2, 3];
+ let mut some_vec = vec![0, 1, 2, 3];
+ let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
+ let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+ let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+ let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
+
+ {
+ // Test `get().unwrap()`
+ let _ = &boxed_slice[1];
+ let _ = &some_slice[0];
+ let _ = &some_vec[0];
+ let _ = &some_vecdeque[0];
+ let _ = &some_hashmap[&1];
+ let _ = &some_btreemap[&1];
+ #[allow(clippy::unwrap_used)]
+ let _ = false_positive.get(0).unwrap();
+ // Test with deref
+ let _: u8 = boxed_slice[1];
+ }
+
+ {
+ // Test `get_mut().unwrap()`
+ boxed_slice[0] = 1;
+ some_slice[0] = 1;
+ some_vec[0] = 1;
+ some_vecdeque[0] = 1;
+ // Check false positives
+ #[allow(clippy::unwrap_used)]
+ {
+ *some_hashmap.get_mut(&1).unwrap() = 'b';
+ *some_btreemap.get_mut(&1).unwrap() = 'b';
+ *false_positive.get_mut(0).unwrap() = 1;
+ }
+ }
+
+ {
+ // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
+ let _ = some_vec[0..1].to_vec();
+ let _ = some_vec[0..1].to_vec();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs
new file mode 100644
index 000000000..a2a323c14
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_unwrap.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
+#![warn(clippy::unwrap_used)]
+#![deny(clippy::get_unwrap)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+struct GetFalsePositive {
+ arr: [u32; 3],
+}
+
+impl GetFalsePositive {
+ fn get(&self, pos: usize) -> Option<&u32> {
+ self.arr.get(pos)
+ }
+ fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
+ self.arr.get_mut(pos)
+ }
+}
+
+fn main() {
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut some_slice = &mut [0, 1, 2, 3];
+ let mut some_vec = vec![0, 1, 2, 3];
+ let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
+ let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+ let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+ let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
+
+ {
+ // Test `get().unwrap()`
+ let _ = boxed_slice.get(1).unwrap();
+ let _ = some_slice.get(0).unwrap();
+ let _ = some_vec.get(0).unwrap();
+ let _ = some_vecdeque.get(0).unwrap();
+ let _ = some_hashmap.get(&1).unwrap();
+ let _ = some_btreemap.get(&1).unwrap();
+ #[allow(clippy::unwrap_used)]
+ let _ = false_positive.get(0).unwrap();
+ // Test with deref
+ let _: u8 = *boxed_slice.get(1).unwrap();
+ }
+
+ {
+ // Test `get_mut().unwrap()`
+ *boxed_slice.get_mut(0).unwrap() = 1;
+ *some_slice.get_mut(0).unwrap() = 1;
+ *some_vec.get_mut(0).unwrap() = 1;
+ *some_vecdeque.get_mut(0).unwrap() = 1;
+ // Check false positives
+ #[allow(clippy::unwrap_used)]
+ {
+ *some_hashmap.get_mut(&1).unwrap() = 'b';
+ *some_btreemap.get_mut(&1).unwrap() = 'b';
+ *false_positive.get_mut(0).unwrap() = 1;
+ }
+ }
+
+ {
+ // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
+ let _ = some_vec.get(0..1).unwrap().to_vec();
+ let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr
new file mode 100644
index 000000000..ea8fec527
--- /dev/null
+++ b/src/tools/clippy/tests/ui/get_unwrap.stderr
@@ -0,0 +1,191 @@
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:35:17
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
+ |
+note: the lint level is defined here
+ --> $DIR/get_unwrap.rs:5:9
+ |
+LL | #![deny(clippy::get_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:35:17
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unwrap-used` implied by `-D warnings`
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:36:17
+ |
+LL | let _ = some_slice.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:36:17
+ |
+LL | let _ = some_slice.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:37:17
+ |
+LL | let _ = some_vec.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:37:17
+ |
+LL | let _ = some_vec.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:38:17
+ |
+LL | let _ = some_vecdeque.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:38:17
+ |
+LL | let _ = some_vecdeque.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:39:17
+ |
+LL | let _ = some_hashmap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:39:17
+ |
+LL | let _ = some_hashmap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:40:17
+ |
+LL | let _ = some_btreemap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:40:17
+ |
+LL | let _ = some_btreemap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:44:21
+ |
+LL | let _: u8 = *boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:44:22
+ |
+LL | let _: u8 = *boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:49:9
+ |
+LL | *boxed_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:49:10
+ |
+LL | *boxed_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:50:9
+ |
+LL | *some_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:50:10
+ |
+LL | *some_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:51:9
+ |
+LL | *some_vec.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:51:10
+ |
+LL | *some_vec.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:52:9
+ |
+LL | *some_vecdeque.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:52:10
+ |
+LL | *some_vecdeque.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:64:17
+ |
+LL | let _ = some_vec.get(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:64:17
+ |
+LL | let _ = some_vec.get(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/get_unwrap.rs:65:17
+ |
+LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:65:17
+ |
+LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: aborting due to 26 previous errors
+
diff --git a/src/tools/clippy/tests/ui/identity_op.fixed b/src/tools/clippy/tests/ui/identity_op.fixed
new file mode 100644
index 000000000..5f9cebe21
--- /dev/null
+++ b/src/tools/clippy/tests/ui/identity_op.fixed
@@ -0,0 +1,119 @@
+// run-rustfix
+
+#![warn(clippy::identity_op)]
+#![allow(
+ clippy::eq_op,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::op_ref,
+ clippy::double_parens,
+ unused
+)]
+
+use std::fmt::Write as _;
+
+const ONE: i64 = 1;
+const NEG_ONE: i64 = -1;
+const ZERO: i64 = 0;
+
+struct A(String);
+
+impl std::ops::Shl<i32> for A {
+ type Output = A;
+ fn shl(mut self, other: i32) -> Self {
+ let _ = write!(self.0, "{}", other);
+ self
+ }
+}
+
+struct Length(u8);
+struct Meter;
+
+impl core::ops::Mul<Meter> for u8 {
+ type Output = Length;
+ fn mul(self, _: Meter) -> Length {
+ Length(self)
+ }
+}
+
+#[rustfmt::skip]
+fn main() {
+ let x = 0;
+
+ x;
+ x;
+ x + 1;
+ x;
+ 1 + x;
+ x - ZERO; //no error, as we skip lookups (for now)
+ x;
+ ((ZERO)) | x; //no error, as we skip lookups (for now)
+
+ x;
+ x;
+ x / ONE; //no error, as we skip lookups (for now)
+
+ x / 2; //no false positive
+
+ x & NEG_ONE; //no error, as we skip lookups (for now)
+ x;
+
+ let u: u8 = 0;
+ u;
+
+ 1 << 0; // no error, this case is allowed, see issue 3430
+ 42;
+ 1;
+ 42;
+ &x;
+ x;
+
+ let mut a = A("".into());
+ let b = a << 0; // no error: non-integer
+
+ 1 * Meter; // no error: non-integer
+
+ 2;
+ -2;
+ 2 + x;
+ -2 + x;
+ x + 1;
+ (x + 1) % 3; // no error
+ 4 % 3; // no error
+ 4 % -3; // no error
+
+ // See #8724
+ let a = 0;
+ let b = true;
+ (if b { 1 } else { 2 });
+ (if b { 1 } else { 2 }) + if b { 3 } else { 4 };
+ (match a { 0 => 10, _ => 20 });
+ (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
+ (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
+ (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
+ (if b { 1 } else { 2 });
+
+ ({ a }) + 3;
+ ({ a } * 2);
+ (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
+
+ fn f(_: i32) {
+ todo!();
+ }
+ f(a + { 8 * 5 });
+ f(if b { 1 } else { 2 } + 3);
+ const _: i32 = { 2 * 4 } + 3;
+ const _: i32 = { 1 + 2 * 3 } + 3;
+
+ a as usize;
+ let _ = a as usize;
+ ({ a } as usize);
+
+ 2 * { a };
+ (({ a } + 4));
+ 1;
+}
+
+pub fn decide(a: bool, b: bool) -> u32 {
+ (if a { 1 } else { 2 }) + if b { 3 } else { 5 }
+}
diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs
new file mode 100644
index 000000000..ca799c9cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/identity_op.rs
@@ -0,0 +1,119 @@
+// run-rustfix
+
+#![warn(clippy::identity_op)]
+#![allow(
+ clippy::eq_op,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::op_ref,
+ clippy::double_parens,
+ unused
+)]
+
+use std::fmt::Write as _;
+
+const ONE: i64 = 1;
+const NEG_ONE: i64 = -1;
+const ZERO: i64 = 0;
+
+struct A(String);
+
+impl std::ops::Shl<i32> for A {
+ type Output = A;
+ fn shl(mut self, other: i32) -> Self {
+ let _ = write!(self.0, "{}", other);
+ self
+ }
+}
+
+struct Length(u8);
+struct Meter;
+
+impl core::ops::Mul<Meter> for u8 {
+ type Output = Length;
+ fn mul(self, _: Meter) -> Length {
+ Length(self)
+ }
+}
+
+#[rustfmt::skip]
+fn main() {
+ let x = 0;
+
+ x + 0;
+ x + (1 - 1);
+ x + 1;
+ 0 + x;
+ 1 + x;
+ x - ZERO; //no error, as we skip lookups (for now)
+ x | (0);
+ ((ZERO)) | x; //no error, as we skip lookups (for now)
+
+ x * 1;
+ 1 * x;
+ x / ONE; //no error, as we skip lookups (for now)
+
+ x / 2; //no false positive
+
+ x & NEG_ONE; //no error, as we skip lookups (for now)
+ -1 & x;
+
+ let u: u8 = 0;
+ u & 255;
+
+ 1 << 0; // no error, this case is allowed, see issue 3430
+ 42 << 0;
+ 1 >> 0;
+ 42 >> 0;
+ &x >> 0;
+ x >> &0;
+
+ let mut a = A("".into());
+ let b = a << 0; // no error: non-integer
+
+ 1 * Meter; // no error: non-integer
+
+ 2 % 3;
+ -2 % 3;
+ 2 % -3 + x;
+ -2 % -3 + x;
+ x + 1 % 3;
+ (x + 1) % 3; // no error
+ 4 % 3; // no error
+ 4 % -3; // no error
+
+ // See #8724
+ let a = 0;
+ let b = true;
+ 0 + if b { 1 } else { 2 };
+ 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
+ 0 + match a { 0 => 10, _ => 20 };
+ 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
+ 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
+ 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
+ (if b { 1 } else { 2 }) + 0;
+
+ 0 + { a } + 3;
+ 0 + { a } * 2;
+ 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
+
+ fn f(_: i32) {
+ todo!();
+ }
+ f(1 * a + { 8 * 5 });
+ f(0 + if b { 1 } else { 2 } + 3);
+ const _: i32 = { 2 * 4 } + 0 + 3;
+ const _: i32 = 0 + { 1 + 2 * 3 } + 3;
+
+ 0 + a as usize;
+ let _ = 0 + a as usize;
+ 0 + { a } as usize;
+
+ 2 * (0 + { a });
+ 1 * ({ a } + 4);
+ 1 * 1;
+}
+
+pub fn decide(a: bool, b: bool) -> u32 {
+ 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
+}
diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr
new file mode 100644
index 000000000..1a104a20b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/identity_op.stderr
@@ -0,0 +1,238 @@
+error: this operation has no effect
+ --> $DIR/identity_op.rs:43:5
+ |
+LL | x + 0;
+ | ^^^^^ help: consider reducing it to: `x`
+ |
+ = note: `-D clippy::identity-op` implied by `-D warnings`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:44:5
+ |
+LL | x + (1 - 1);
+ | ^^^^^^^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:46:5
+ |
+LL | 0 + x;
+ | ^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:49:5
+ |
+LL | x | (0);
+ | ^^^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:52:5
+ |
+LL | x * 1;
+ | ^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:53:5
+ |
+LL | 1 * x;
+ | ^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:59:5
+ |
+LL | -1 & x;
+ | ^^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:62:5
+ |
+LL | u & 255;
+ | ^^^^^^^ help: consider reducing it to: `u`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:65:5
+ |
+LL | 42 << 0;
+ | ^^^^^^^ help: consider reducing it to: `42`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:66:5
+ |
+LL | 1 >> 0;
+ | ^^^^^^ help: consider reducing it to: `1`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:67:5
+ |
+LL | 42 >> 0;
+ | ^^^^^^^ help: consider reducing it to: `42`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:68:5
+ |
+LL | &x >> 0;
+ | ^^^^^^^ help: consider reducing it to: `&x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:69:5
+ |
+LL | x >> &0;
+ | ^^^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:76:5
+ |
+LL | 2 % 3;
+ | ^^^^^ help: consider reducing it to: `2`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:77:5
+ |
+LL | -2 % 3;
+ | ^^^^^^ help: consider reducing it to: `-2`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:78:5
+ |
+LL | 2 % -3 + x;
+ | ^^^^^^ help: consider reducing it to: `2`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:79:5
+ |
+LL | -2 % -3 + x;
+ | ^^^^^^^ help: consider reducing it to: `-2`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:80:9
+ |
+LL | x + 1 % 3;
+ | ^^^^^ help: consider reducing it to: `1`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:88:5
+ |
+LL | 0 + if b { 1 } else { 2 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:89:5
+ |
+LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:90:5
+ |
+LL | 0 + match a { 0 => 10, _ => 20 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:91:5
+ |
+LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:92:5
+ |
+LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:93:5
+ |
+LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:94:5
+ |
+LL | (if b { 1 } else { 2 }) + 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:96:5
+ |
+LL | 0 + { a } + 3;
+ | ^^^^^^^^^ help: consider reducing it to: `({ a })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:97:5
+ |
+LL | 0 + { a } * 2;
+ | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:98:5
+ |
+LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:103:7
+ |
+LL | f(1 * a + { 8 * 5 });
+ | ^^^^^ help: consider reducing it to: `a`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:104:7
+ |
+LL | f(0 + if b { 1 } else { 2 } + 3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:105:20
+ |
+LL | const _: i32 = { 2 * 4 } + 0 + 3;
+ | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:106:20
+ |
+LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3;
+ | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:108:5
+ |
+LL | 0 + a as usize;
+ | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:109:13
+ |
+LL | let _ = 0 + a as usize;
+ | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:110:5
+ |
+LL | 0 + { a } as usize;
+ | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:112:9
+ |
+LL | 2 * (0 + { a });
+ | ^^^^^^^^^^^ help: consider reducing it to: `{ a }`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:113:5
+ |
+LL | 1 * ({ a } + 4);
+ | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:114:5
+ |
+LL | 1 * 1;
+ | ^^^^^ help: consider reducing it to: `1`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:118:5
+ |
+LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
+
+error: aborting due to 39 previous errors
+
diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs
new file mode 100644
index 000000000..6cbfafbb3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_let_mutex.rs
@@ -0,0 +1,42 @@
+#![warn(clippy::if_let_mutex)]
+
+use std::ops::Deref;
+use std::sync::Mutex;
+
+fn do_stuff<T>(_: T) {}
+
+fn if_let() {
+ let m = Mutex::new(1_u8);
+ if let Err(locked) = m.lock() {
+ do_stuff(locked);
+ } else {
+ let lock = m.lock().unwrap();
+ do_stuff(lock);
+ };
+}
+
+// This is the most common case as the above case is pretty
+// contrived.
+fn if_let_option() {
+ let m = Mutex::new(Some(0_u8));
+ if let Some(locked) = m.lock().unwrap().deref() {
+ do_stuff(locked);
+ } else {
+ let lock = m.lock().unwrap();
+ do_stuff(lock);
+ };
+}
+
+// When mutexes are different don't warn
+fn if_let_different_mutex() {
+ let m = Mutex::new(Some(0_u8));
+ let other = Mutex::new(None::<u8>);
+ if let Some(locked) = m.lock().unwrap().deref() {
+ do_stuff(locked);
+ } else {
+ let lock = other.lock().unwrap();
+ do_stuff(lock);
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.stderr
new file mode 100644
index 000000000..e9c4d9163
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_let_mutex.stderr
@@ -0,0 +1,29 @@
+error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
+ --> $DIR/if_let_mutex.rs:10:5
+ |
+LL | / if let Err(locked) = m.lock() {
+LL | | do_stuff(locked);
+LL | | } else {
+LL | | let lock = m.lock().unwrap();
+LL | | do_stuff(lock);
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::if-let-mutex` implied by `-D warnings`
+ = help: move the lock call outside of the `if let ...` expression
+
+error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
+ --> $DIR/if_let_mutex.rs:22:5
+ |
+LL | / if let Some(locked) = m.lock().unwrap().deref() {
+LL | | do_stuff(locked);
+LL | | } else {
+LL | | let lock = m.lock().unwrap();
+LL | | do_stuff(lock);
+LL | | };
+ | |_____^
+ |
+ = help: move the lock call outside of the `if let ...` expression
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs
new file mode 100644
index 000000000..b7012b43d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_not_else.rs
@@ -0,0 +1,29 @@
+#![warn(clippy::all)]
+#![warn(clippy::if_not_else)]
+
+fn foo() -> bool {
+ unimplemented!()
+}
+fn bla() -> bool {
+ unimplemented!()
+}
+
+fn main() {
+ if !bla() {
+ println!("Bugs");
+ } else {
+ println!("Bunny");
+ }
+ if 4 != 5 {
+ println!("Bugs");
+ } else {
+ println!("Bunny");
+ }
+ if !foo() {
+ println!("Foo");
+ } else if !bla() {
+ println!("Bugs");
+ } else {
+ println!("Bunny");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr
new file mode 100644
index 000000000..8c8cc44bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_not_else.stderr
@@ -0,0 +1,27 @@
+error: unnecessary boolean `not` operation
+ --> $DIR/if_not_else.rs:12:5
+ |
+LL | / if !bla() {
+LL | | println!("Bugs");
+LL | | } else {
+LL | | println!("Bunny");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::if-not-else` implied by `-D warnings`
+ = help: remove the `!` and swap the blocks of the `if`/`else`
+
+error: unnecessary `!=` operation
+ --> $DIR/if_not_else.rs:17:5
+ |
+LL | / if 4 != 5 {
+LL | | println!("Bugs");
+LL | | } else {
+LL | | println!("Bunny");
+LL | | }
+ | |_____^
+ |
+ = help: change to `==` and swap the blocks of the `if`/`else`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs
new file mode 100644
index 000000000..2598c2ab4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_same_then_else.rs
@@ -0,0 +1,217 @@
+#![warn(clippy::if_same_then_else)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::eq_op,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unused_unit,
+ clippy::zero_divided_by_zero,
+ clippy::branches_sharing_code,
+ dead_code,
+ unreachable_code
+)]
+
+struct Foo {
+ bar: u8,
+}
+
+fn foo() -> bool {
+ unimplemented!()
+}
+
+fn if_same_then_else() {
+ if true {
+ Foo { bar: 42 };
+ 0..10;
+ ..;
+ 0..;
+ ..10;
+ 0..=10;
+ foo();
+ } else {
+ //~ ERROR same body as `if` block
+ Foo { bar: 42 };
+ 0..10;
+ ..;
+ 0..;
+ ..10;
+ 0..=10;
+ foo();
+ }
+
+ if true {
+ Foo { bar: 42 };
+ } else {
+ Foo { bar: 43 };
+ }
+
+ if true {
+ ();
+ } else {
+ ()
+ }
+
+ if true {
+ 0..10;
+ } else {
+ 0..=10;
+ }
+
+ if true {
+ foo();
+ foo();
+ } else {
+ foo();
+ }
+
+ let _ = if true {
+ 0.0
+ } else {
+ //~ ERROR same body as `if` block
+ 0.0
+ };
+
+ let _ = if true {
+ -0.0
+ } else {
+ //~ ERROR same body as `if` block
+ -0.0
+ };
+
+ let _ = if true { 0.0 } else { -0.0 };
+
+ // Different NaNs
+ let _ = if true { 0.0 / 0.0 } else { f32::NAN };
+
+ if true {
+ foo();
+ }
+
+ let _ = if true {
+ 42
+ } else {
+ //~ ERROR same body as `if` block
+ 42
+ };
+
+ if true {
+ let bar = if true { 42 } else { 43 };
+
+ while foo() {
+ break;
+ }
+ bar + 1;
+ } else {
+ //~ ERROR same body as `if` block
+ let bar = if true { 42 } else { 43 };
+
+ while foo() {
+ break;
+ }
+ bar + 1;
+ }
+
+ if true {
+ let _ = match 42 {
+ 42 => 1,
+ a if a > 0 => 2,
+ 10..=15 => 3,
+ _ => 4,
+ };
+ } else if false {
+ foo();
+ } else if foo() {
+ let _ = match 42 {
+ 42 => 1,
+ a if a > 0 => 2,
+ 10..=15 => 3,
+ _ => 4,
+ };
+ }
+}
+
+// Issue #2423. This was causing an ICE.
+fn func() {
+ if true {
+ f(&[0; 62]);
+ f(&[0; 4]);
+ f(&[0; 3]);
+ } else {
+ f(&[0; 62]);
+ f(&[0; 6]);
+ f(&[0; 6]);
+ }
+}
+
+fn f(val: &[u8]) {}
+
+mod issue_5698 {
+ fn mul_not_always_commutative(x: i32, y: i32) -> i32 {
+ if x == 42 {
+ x * y
+ } else if x == 21 {
+ y * x
+ } else {
+ 0
+ }
+ }
+}
+
+mod issue_8836 {
+ fn do_not_lint() {
+ if true {
+ todo!()
+ } else {
+ todo!()
+ }
+ if true {
+ todo!();
+ } else {
+ todo!();
+ }
+ if true {
+ unimplemented!()
+ } else {
+ unimplemented!()
+ }
+ if true {
+ unimplemented!();
+ } else {
+ unimplemented!();
+ }
+
+ if true {
+ println!("FOO");
+ todo!();
+ } else {
+ println!("FOO");
+ todo!();
+ }
+
+ if true {
+ println!("FOO");
+ unimplemented!();
+ } else {
+ println!("FOO");
+ unimplemented!();
+ }
+
+ if true {
+ println!("FOO");
+ todo!()
+ } else {
+ println!("FOO");
+ todo!()
+ }
+
+ if true {
+ println!("FOO");
+ unimplemented!()
+ } else {
+ println!("FOO");
+ unimplemented!()
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr
new file mode 100644
index 000000000..2cdf44248
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr
@@ -0,0 +1,112 @@
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else.rs:23:13
+ |
+LL | if true {
+ | _____________^
+LL | | Foo { bar: 42 };
+LL | | 0..10;
+LL | | ..;
+... |
+LL | | foo();
+LL | | } else {
+ | |_____^
+ |
+ = note: `-D clippy::if-same-then-else` implied by `-D warnings`
+note: same as this
+ --> $DIR/if_same_then_else.rs:31:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | Foo { bar: 42 };
+LL | | 0..10;
+... |
+LL | | foo();
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else.rs:67:21
+ |
+LL | let _ = if true {
+ | _____________________^
+LL | | 0.0
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else.rs:69:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | 0.0
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else.rs:74:21
+ |
+LL | let _ = if true {
+ | _____________________^
+LL | | -0.0
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else.rs:76:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | -0.0
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else.rs:90:21
+ |
+LL | let _ = if true {
+ | _____________________^
+LL | | 42
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else.rs:92:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | 42
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else.rs:97:13
+ |
+LL | if true {
+ | _____________^
+LL | | let bar = if true { 42 } else { 43 };
+LL | |
+LL | | while foo() {
+... |
+LL | | bar + 1;
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else.rs:104:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | let bar = if true { 42 } else { 43 };
+LL | |
+... |
+LL | | bar + 1;
+LL | | }
+ | |_____^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs
new file mode 100644
index 000000000..0016009a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs
@@ -0,0 +1,160 @@
+#![warn(clippy::if_same_then_else)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::collapsible_else_if,
+ clippy::equatable_if_let,
+ clippy::collapsible_if,
+ clippy::ifs_same_cond,
+ clippy::needless_return,
+ clippy::single_element_loop,
+ clippy::branches_sharing_code
+)]
+
+fn if_same_then_else2() -> Result<&'static str, ()> {
+ if true {
+ for _ in &[42] {
+ let foo: &Option<_> = &Some::<u8>(42);
+ if foo.is_some() {
+ break;
+ } else {
+ continue;
+ }
+ }
+ } else {
+ //~ ERROR same body as `if` block
+ for _ in &[42] {
+ let bar: &Option<_> = &Some::<u8>(42);
+ if bar.is_some() {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if true {
+ if let Some(a) = Some(42) {}
+ } else {
+ //~ ERROR same body as `if` block
+ if let Some(a) = Some(42) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ //~ ERROR same body as `if` block
+ if let (1, .., 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 4) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let (1, .., 3) = (1, 2, 3) {}
+ } else {
+ if let (.., 1, 3) = (1, 2, 3) {}
+ }
+
+ if true {
+ if let Some(42) = None {}
+ } else {
+ if let Option::Some(42) = None {}
+ }
+
+ if true {
+ if let Some(42) = None::<u8> {}
+ } else {
+ if let Some(42) = None {}
+ }
+
+ if true {
+ if let Some(42) = None::<u8> {}
+ } else {
+ if let Some(42) = None::<u32> {}
+ }
+
+ if true {
+ if let Some(a) = Some(42) {}
+ } else {
+ if let Some(a) = Some(43) {}
+ }
+
+ // Same NaNs
+ let _ = if true {
+ f32::NAN
+ } else {
+ //~ ERROR same body as `if` block
+ f32::NAN
+ };
+
+ if true {
+ Ok("foo")?;
+ } else {
+ //~ ERROR same body as `if` block
+ Ok("foo")?;
+ }
+
+ if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else if false {
+ let foo = "bar";
+ return Ok(&foo[0..]);
+ } else {
+ let foo = "";
+ return Ok(&foo[0..]);
+ }
+
+ if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else if false {
+ let foo = "bar";
+ return Ok(&foo[0..]);
+ } else if true {
+ let foo = "";
+ return Ok(&foo[0..]);
+ } else {
+ let foo = "";
+ return Ok(&foo[0..]);
+ }
+
+ // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559.
+ if true {
+ let foo = "";
+ let (x, y) = (1, 2);
+ return Ok(&foo[x..y]);
+ } else {
+ let foo = "";
+ let (y, x) = (1, 2);
+ return Ok(&foo[x..y]);
+ }
+
+ // Issue #7579
+ let _ = if let Some(0) = None { 0 } else { 0 };
+
+ if true {
+ return Err(());
+ } else if let Some(0) = None {
+ return Err(());
+ }
+
+ let _ = if let Some(0) = None {
+ 0
+ } else if let Some(1) = None {
+ 0
+ } else {
+ 0
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
new file mode 100644
index 000000000..cac788f85
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
@@ -0,0 +1,125 @@
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:14:13
+ |
+LL | if true {
+ | _____________^
+LL | | for _ in &[42] {
+LL | | let foo: &Option<_> = &Some::<u8>(42);
+LL | | if foo.is_some() {
+... |
+LL | | }
+LL | | } else {
+ | |_____^
+ |
+ = note: `-D clippy::if-same-then-else` implied by `-D warnings`
+note: same as this
+ --> $DIR/if_same_then_else2.rs:23:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | for _ in &[42] {
+LL | | let bar: &Option<_> = &Some::<u8>(42);
+... |
+LL | | }
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:35:13
+ |
+LL | if true {
+ | _____________^
+LL | | if let Some(a) = Some(42) {}
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else2.rs:37:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | if let Some(a) = Some(42) {}
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:42:13
+ |
+LL | if true {
+ | _____________^
+LL | | if let (1, .., 3) = (1, 2, 3) {}
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else2.rs:44:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | if let (1, .., 3) = (1, 2, 3) {}
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:92:21
+ |
+LL | let _ = if true {
+ | _____________________^
+LL | | f32::NAN
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else2.rs:94:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | f32::NAN
+LL | | };
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:99:13
+ |
+LL | if true {
+ | _____________^
+LL | | Ok("foo")?;
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else2.rs:101:12
+ |
+LL | } else {
+ | ____________^
+LL | | //~ ERROR same body as `if` block
+LL | | Ok("foo")?;
+LL | | }
+ | |_____^
+
+error: this `if` has identical blocks
+ --> $DIR/if_same_then_else2.rs:123:20
+ |
+LL | } else if true {
+ | ____________________^
+LL | | let foo = "";
+LL | | return Ok(&foo[0..]);
+LL | | } else {
+ | |_____^
+ |
+note: same as this
+ --> $DIR/if_same_then_else2.rs:126:12
+ |
+LL | } else {
+ | ____________^
+LL | | let foo = "";
+LL | | return Ok(&foo[0..]);
+LL | | }
+ | |_____^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
new file mode 100644
index 000000000..3bc3a0395
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -0,0 +1,115 @@
+#![warn(clippy::if_then_some_else_none)]
+#![feature(custom_inner_attributes)]
+
+fn main() {
+ // Should issue an error.
+ let _ = if foo() {
+ println!("true!");
+ Some("foo")
+ } else {
+ None
+ };
+
+ // Should issue an error when macros are used.
+ let _ = if matches!(true, true) {
+ println!("true!");
+ Some(matches!(true, false))
+ } else {
+ None
+ };
+
+ // Should issue an error. Binary expression `o < 32` should be parenthesized.
+ let x = Some(5);
+ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+
+ // Should issue an error. Unary expression `!x` should be parenthesized.
+ let x = true;
+ let _ = if !x { Some(0) } else { None };
+
+ // Should not issue an error since the `else` block has a statement besides `None`.
+ let _ = if foo() {
+ println!("true!");
+ Some("foo")
+ } else {
+ eprintln!("false...");
+ None
+ };
+
+ // Should not issue an error since there are more than 2 blocks in the if-else chain.
+ let _ = if foo() {
+ println!("foo true!");
+ Some("foo")
+ } else if bar() {
+ println!("bar true!");
+ Some("bar")
+ } else {
+ None
+ };
+
+ let _ = if foo() {
+ println!("foo true!");
+ Some("foo")
+ } else {
+ bar().then(|| {
+ println!("bar true!");
+ "bar"
+ })
+ };
+
+ // Should not issue an error since the `then` block has `None`, not `Some`.
+ let _ = if foo() { None } else { Some("foo is false") };
+
+ // Should not issue an error since the `else` block doesn't use `None` directly.
+ let _ = if foo() { Some("foo is true") } else { into_none() };
+
+ // Should not issue an error since the `then` block doesn't use `Some` directly.
+ let _ = if foo() { into_some("foo") } else { None };
+}
+
+fn _msrv_1_49() {
+ #![clippy::msrv = "1.49"]
+ // `bool::then` was stabilized in 1.50. Do not lint this
+ let _ = if foo() {
+ println!("true!");
+ Some(149)
+ } else {
+ None
+ };
+}
+
+fn _msrv_1_50() {
+ #![clippy::msrv = "1.50"]
+ let _ = if foo() {
+ println!("true!");
+ Some(150)
+ } else {
+ None
+ };
+}
+
+fn foo() -> bool {
+ unimplemented!()
+}
+
+fn bar() -> bool {
+ unimplemented!()
+}
+
+fn into_some<T>(v: T) -> Option<T> {
+ Some(v)
+}
+
+fn into_none<T>() -> Option<T> {
+ None
+}
+
+// Should not warn
+fn f(b: bool, v: Option<()>) -> Option<()> {
+ if b {
+ v?; // This is a potential early return, is not equivalent with `bool::then`
+
+ Some(())
+ } else {
+ None
+ }
+}
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
new file mode 100644
index 000000000..8cb22d569
--- /dev/null
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
@@ -0,0 +1,61 @@
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:6:13
+ |
+LL | let _ = if foo() {
+ | _____________^
+LL | | println!("true!");
+LL | | Some("foo")
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
+ = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:14:13
+ |
+LL | let _ = if matches!(true, true) {
+ | _____________^
+LL | | println!("true!");
+LL | | Some(matches!(true, false))
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:23:28
+ |
+LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `bool::then` like: `(o < 32).then(|| o)`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:27:13
+ |
+LL | let _ = if !x { Some(0) } else { None };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `bool::then` like: `(!x).then(|| 0)`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:82:13
+ |
+LL | let _ = if foo() {
+ | _____________^
+LL | | println!("true!");
+LL | | Some(150)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs
new file mode 100644
index 000000000..80e9839ff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs
@@ -0,0 +1,46 @@
+#![warn(clippy::ifs_same_cond)]
+#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
+
+fn ifs_same_cond() {
+ let a = 0;
+ let b = false;
+
+ if b {
+ } else if b {
+ //~ ERROR ifs same condition
+ }
+
+ if a == 1 {
+ } else if a == 1 {
+ //~ ERROR ifs same condition
+ }
+
+ if 2 * a == 1 {
+ } else if 2 * a == 2 {
+ } else if 2 * a == 1 {
+ //~ ERROR ifs same condition
+ } else if a == 1 {
+ }
+
+ // See #659
+ if cfg!(feature = "feature1-659") {
+ 1
+ } else if cfg!(feature = "feature2-659") {
+ 2
+ } else {
+ 3
+ };
+
+ let mut v = vec![1];
+ if v.pop() == None {
+ // ok, functions
+ } else if v.pop() == None {
+ }
+
+ if v.len() == 42 {
+ // ok, functions
+ } else if v.len() == 42 {
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
new file mode 100644
index 000000000..0c8f49b86
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
@@ -0,0 +1,39 @@
+error: this `if` has the same condition as a previous `if`
+ --> $DIR/ifs_same_cond.rs:9:15
+ |
+LL | } else if b {
+ | ^
+ |
+ = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
+note: same as this
+ --> $DIR/ifs_same_cond.rs:8:8
+ |
+LL | if b {
+ | ^
+
+error: this `if` has the same condition as a previous `if`
+ --> $DIR/ifs_same_cond.rs:14:15
+ |
+LL | } else if a == 1 {
+ | ^^^^^^
+ |
+note: same as this
+ --> $DIR/ifs_same_cond.rs:13:8
+ |
+LL | if a == 1 {
+ | ^^^^^^
+
+error: this `if` has the same condition as a previous `if`
+ --> $DIR/ifs_same_cond.rs:20:15
+ |
+LL | } else if 2 * a == 1 {
+ | ^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/ifs_same_cond.rs:18:8
+ |
+LL | if 2 * a == 1 {
+ | ^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs
new file mode 100644
index 000000000..aea52a852
--- /dev/null
+++ b/src/tools/clippy/tests/ui/impl.rs
@@ -0,0 +1,67 @@
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
+#![warn(clippy::multiple_inherent_impl)]
+
+struct MyStruct;
+
+impl MyStruct {
+ fn first() {}
+}
+
+impl MyStruct {
+ fn second() {}
+}
+
+impl<'a> MyStruct {
+ fn lifetimed() {}
+}
+
+mod submod {
+ struct MyStruct;
+ impl MyStruct {
+ fn other() {}
+ }
+
+ impl super::MyStruct {
+ fn third() {}
+ }
+}
+
+use std::fmt;
+impl fmt::Debug for MyStruct {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MyStruct {{ }}")
+ }
+}
+
+// issue #5772
+struct WithArgs<T>(T);
+impl WithArgs<u32> {
+ fn f1() {}
+}
+impl WithArgs<u64> {
+ fn f2() {}
+}
+impl WithArgs<u64> {
+ fn f3() {}
+}
+
+// Ok, the struct is allowed to have multiple impls.
+#[allow(clippy::multiple_inherent_impl)]
+struct Allowed;
+impl Allowed {}
+impl Allowed {}
+impl Allowed {}
+
+struct AllowedImpl;
+#[allow(clippy::multiple_inherent_impl)]
+impl AllowedImpl {}
+// Ok, the first block is skipped by this lint.
+impl AllowedImpl {}
+
+struct OneAllowedImpl;
+impl OneAllowedImpl {}
+#[allow(clippy::multiple_inherent_impl)]
+impl OneAllowedImpl {}
+impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed.
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr
new file mode 100644
index 000000000..8703ecac9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/impl.stderr
@@ -0,0 +1,63 @@
+error: multiple implementations of this structure
+ --> $DIR/impl.rs:10:1
+ |
+LL | / impl MyStruct {
+LL | | fn second() {}
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings`
+note: first implementation here
+ --> $DIR/impl.rs:6:1
+ |
+LL | / impl MyStruct {
+LL | | fn first() {}
+LL | | }
+ | |_^
+
+error: multiple implementations of this structure
+ --> $DIR/impl.rs:24:5
+ |
+LL | / impl super::MyStruct {
+LL | | fn third() {}
+LL | | }
+ | |_____^
+ |
+note: first implementation here
+ --> $DIR/impl.rs:6:1
+ |
+LL | / impl MyStruct {
+LL | | fn first() {}
+LL | | }
+ | |_^
+
+error: multiple implementations of this structure
+ --> $DIR/impl.rs:44:1
+ |
+LL | / impl WithArgs<u64> {
+LL | | fn f3() {}
+LL | | }
+ | |_^
+ |
+note: first implementation here
+ --> $DIR/impl.rs:41:1
+ |
+LL | / impl WithArgs<u64> {
+LL | | fn f2() {}
+LL | | }
+ | |_^
+
+error: multiple implementations of this structure
+ --> $DIR/impl.rs:65:1
+ |
+LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed.
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first implementation here
+ --> $DIR/impl.rs:62:1
+ |
+LL | impl OneAllowedImpl {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed
new file mode 100644
index 000000000..33770fc2a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_clone.fixed
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::implicit_clone)]
+#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
+use std::borrow::Borrow;
+use std::ffi::{OsStr, OsString};
+use std::path::PathBuf;
+
+fn return_owned_from_slice(slice: &[u32]) -> Vec<u32> {
+ slice.to_owned()
+}
+
+pub fn own_same<T>(v: T) -> T
+where
+ T: ToOwned<Owned = T>,
+{
+ v.to_owned()
+}
+
+pub fn own_same_from_ref<T>(v: &T) -> T
+where
+ T: ToOwned<Owned = T>,
+{
+ v.to_owned()
+}
+
+pub fn own_different<T, U>(v: T) -> U
+where
+ T: ToOwned<Owned = U>,
+{
+ v.to_owned()
+}
+
+#[derive(Copy, Clone)]
+struct Kitten;
+impl Kitten {
+ // badly named method
+ fn to_vec(self) -> Kitten {
+ Kitten {}
+ }
+}
+impl Borrow<BorrowedKitten> for Kitten {
+ fn borrow(&self) -> &BorrowedKitten {
+ static VALUE: BorrowedKitten = BorrowedKitten {};
+ &VALUE
+ }
+}
+
+struct BorrowedKitten;
+impl ToOwned for BorrowedKitten {
+ type Owned = Kitten;
+ fn to_owned(&self) -> Kitten {
+ Kitten {}
+ }
+}
+
+mod weird {
+ #[allow(clippy::ptr_arg)]
+ pub fn to_vec(v: &Vec<u32>) -> Vec<u32> {
+ v.clone()
+ }
+}
+
+fn main() {
+ let vec = vec![5];
+ let _ = return_owned_from_slice(&vec);
+ let _ = vec.clone();
+ let _ = vec.clone();
+
+ let vec_ref = &vec;
+ let _ = return_owned_from_slice(vec_ref);
+ let _ = vec_ref.clone();
+ let _ = vec_ref.clone();
+
+ // we expect no lint for this
+ let _ = weird::to_vec(&vec);
+
+ // we expect no lints for this
+ let slice: &[u32] = &[1, 2, 3, 4, 5];
+ let _ = return_owned_from_slice(slice);
+ let _ = slice.to_owned();
+ let _ = slice.to_vec();
+
+ let str = "hello world".to_string();
+ let _ = str.clone();
+
+ // testing w/ an arbitrary type
+ let kitten = Kitten {};
+ let _ = kitten.clone();
+ let _ = own_same_from_ref(&kitten);
+ // this shouln't lint
+ let _ = kitten.to_vec();
+
+ // we expect no lints for this
+ let borrowed = BorrowedKitten {};
+ let _ = borrowed.to_owned();
+
+ let pathbuf = PathBuf::new();
+ let _ = pathbuf.clone();
+ let _ = pathbuf.clone();
+
+ let os_string = OsString::from("foo");
+ let _ = os_string.clone();
+ let _ = os_string.clone();
+
+ // we expect no lints for this
+ let os_str = OsStr::new("foo");
+ let _ = os_str.to_owned();
+ let _ = os_str.to_os_string();
+
+ // issue #8227
+ let pathbuf_ref = &pathbuf;
+ let pathbuf_ref = &pathbuf_ref;
+ let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf`
+ let _ = (*pathbuf_ref).clone();
+ let pathbuf_ref = &pathbuf_ref;
+ let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
+ let _ = (**pathbuf_ref).clone();
+}
diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs
new file mode 100644
index 000000000..fc896525b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_clone.rs
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::implicit_clone)]
+#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
+use std::borrow::Borrow;
+use std::ffi::{OsStr, OsString};
+use std::path::PathBuf;
+
+fn return_owned_from_slice(slice: &[u32]) -> Vec<u32> {
+ slice.to_owned()
+}
+
+pub fn own_same<T>(v: T) -> T
+where
+ T: ToOwned<Owned = T>,
+{
+ v.to_owned()
+}
+
+pub fn own_same_from_ref<T>(v: &T) -> T
+where
+ T: ToOwned<Owned = T>,
+{
+ v.to_owned()
+}
+
+pub fn own_different<T, U>(v: T) -> U
+where
+ T: ToOwned<Owned = U>,
+{
+ v.to_owned()
+}
+
+#[derive(Copy, Clone)]
+struct Kitten;
+impl Kitten {
+ // badly named method
+ fn to_vec(self) -> Kitten {
+ Kitten {}
+ }
+}
+impl Borrow<BorrowedKitten> for Kitten {
+ fn borrow(&self) -> &BorrowedKitten {
+ static VALUE: BorrowedKitten = BorrowedKitten {};
+ &VALUE
+ }
+}
+
+struct BorrowedKitten;
+impl ToOwned for BorrowedKitten {
+ type Owned = Kitten;
+ fn to_owned(&self) -> Kitten {
+ Kitten {}
+ }
+}
+
+mod weird {
+ #[allow(clippy::ptr_arg)]
+ pub fn to_vec(v: &Vec<u32>) -> Vec<u32> {
+ v.clone()
+ }
+}
+
+fn main() {
+ let vec = vec![5];
+ let _ = return_owned_from_slice(&vec);
+ let _ = vec.to_owned();
+ let _ = vec.to_vec();
+
+ let vec_ref = &vec;
+ let _ = return_owned_from_slice(vec_ref);
+ let _ = vec_ref.to_owned();
+ let _ = vec_ref.to_vec();
+
+ // we expect no lint for this
+ let _ = weird::to_vec(&vec);
+
+ // we expect no lints for this
+ let slice: &[u32] = &[1, 2, 3, 4, 5];
+ let _ = return_owned_from_slice(slice);
+ let _ = slice.to_owned();
+ let _ = slice.to_vec();
+
+ let str = "hello world".to_string();
+ let _ = str.to_owned();
+
+ // testing w/ an arbitrary type
+ let kitten = Kitten {};
+ let _ = kitten.to_owned();
+ let _ = own_same_from_ref(&kitten);
+ // this shouln't lint
+ let _ = kitten.to_vec();
+
+ // we expect no lints for this
+ let borrowed = BorrowedKitten {};
+ let _ = borrowed.to_owned();
+
+ let pathbuf = PathBuf::new();
+ let _ = pathbuf.to_owned();
+ let _ = pathbuf.to_path_buf();
+
+ let os_string = OsString::from("foo");
+ let _ = os_string.to_owned();
+ let _ = os_string.to_os_string();
+
+ // we expect no lints for this
+ let os_str = OsStr::new("foo");
+ let _ = os_str.to_owned();
+ let _ = os_str.to_os_string();
+
+ // issue #8227
+ let pathbuf_ref = &pathbuf;
+ let pathbuf_ref = &pathbuf_ref;
+ let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf`
+ let _ = pathbuf_ref.to_path_buf();
+ let pathbuf_ref = &pathbuf_ref;
+ let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
+ let _ = pathbuf_ref.to_path_buf();
+}
diff --git a/src/tools/clippy/tests/ui/implicit_clone.stderr b/src/tools/clippy/tests/ui/implicit_clone.stderr
new file mode 100644
index 000000000..92c1aa58a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_clone.stderr
@@ -0,0 +1,76 @@
+error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:66:13
+ |
+LL | let _ = vec.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using: `vec.clone()`
+ |
+ = note: `-D clippy::implicit-clone` implied by `-D warnings`
+
+error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type
+ --> $DIR/implicit_clone.rs:67:13
+ |
+LL | let _ = vec.to_vec();
+ | ^^^^^^^^^^^^ help: consider using: `vec.clone()`
+
+error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:71:13
+ |
+LL | let _ = vec_ref.to_owned();
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()`
+
+error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type
+ --> $DIR/implicit_clone.rs:72:13
+ |
+LL | let _ = vec_ref.to_vec();
+ | ^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()`
+
+error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:84:13
+ |
+LL | let _ = str.to_owned();
+ | ^^^^^^^^^^^^^^ help: consider using: `str.clone()`
+
+error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:88:13
+ |
+LL | let _ = kitten.to_owned();
+ | ^^^^^^^^^^^^^^^^^ help: consider using: `kitten.clone()`
+
+error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:98:13
+ |
+LL | let _ = pathbuf.to_owned();
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()`
+
+error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
+ --> $DIR/implicit_clone.rs:99:13
+ |
+LL | let _ = pathbuf.to_path_buf();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()`
+
+error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type
+ --> $DIR/implicit_clone.rs:102:13
+ |
+LL | let _ = os_string.to_owned();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()`
+
+error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type
+ --> $DIR/implicit_clone.rs:103:13
+ |
+LL | let _ = os_string.to_os_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()`
+
+error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
+ --> $DIR/implicit_clone.rs:114:13
+ |
+LL | let _ = pathbuf_ref.to_path_buf();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(*pathbuf_ref).clone()`
+
+error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
+ --> $DIR/implicit_clone.rs:117:13
+ |
+LL | let _ = pathbuf_ref.to_path_buf();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs
new file mode 100644
index 000000000..fd96ca3f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_hasher.rs
@@ -0,0 +1,102 @@
+// aux-build:implicit_hasher_macros.rs
+#![deny(clippy::implicit_hasher)]
+#![allow(unused)]
+
+#[macro_use]
+extern crate implicit_hasher_macros;
+
+use std::cmp::Eq;
+use std::collections::{HashMap, HashSet};
+use std::hash::{BuildHasher, Hash};
+
+pub trait Foo<T>: Sized {
+ fn make() -> (Self, Self);
+}
+
+impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ // OK, don't suggest to modify these
+ let _: HashMap<i32, i32> = HashMap::new();
+ let _: HashSet<i32> = HashSet::new();
+
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ fn make() -> (Self, Self) {
+ ((HashMap::new(),), (HashMap::with_capacity(10),))
+ }
+}
+impl Foo<i16> for HashMap<String, String> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+}
+
+impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
+ fn make() -> (Self, Self) {
+ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+impl Foo<i16> for HashSet<String> {
+ fn make() -> (Self, Self) {
+ (HashSet::new(), HashSet::with_capacity(10))
+ }
+}
+
+impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
+ fn make() -> (Self, Self) {
+ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+ }
+}
+
+pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+
+macro_rules! gen {
+ (impl) => {
+ impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ fn make() -> (Self, Self) {
+ (HashMap::new(), HashMap::with_capacity(10))
+ }
+ }
+ };
+
+ (fn $name:ident) => {
+ pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ };
+}
+#[rustfmt::skip]
+gen!(impl);
+gen!(fn bar);
+
+// When the macro is in a different file, the suggestion spans can't be combined properly
+// and should not cause an ICE
+// See #2707
+#[macro_use]
+#[path = "auxiliary/test_macro.rs"]
+pub mod test_macro;
+__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
+
+// #4260
+implicit_hasher_fn!();
+
+// #7712
+pub async fn election_vote(_data: HashMap<i32, i32>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr
new file mode 100644
index 000000000..59b0fba2a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr
@@ -0,0 +1,164 @@
+error: impl for `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:16:35
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/implicit_hasher.rs:2:9
+ |
+LL | #![deny(clippy::implicit_hasher)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:25:36
+ |
+LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+ | ^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:30:19
+ |
+LL | impl Foo<i16> for HashMap<String, String> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:47:32
+ |
+LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+ | ^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: impl for `HashSet` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:52:19
+ |
+LL | impl Foo<i16> for HashSet<String> {
+ | ^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
+ | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:69:23
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:69:53
+ |
+LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
+error: impl for `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:73:43
+ |
+LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+ | ^^^^^^^^^^^^^
+...
+LL | gen!(impl);
+ | ---------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+help: ...and use generic constructor
+ |
+LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+ | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:81:33
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: parameter of type `HashSet` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:81:63
+ |
+LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
+...
+LL | gen!(fn bar);
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider adding a type parameter
+ |
+LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+
+error: parameter of type `HashMap` should be generalized over different hashers
+ --> $DIR/implicit_hasher.rs:100:35
+ |
+LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a type parameter
+ |
+LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed
new file mode 100644
index 000000000..5e55b8b67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_return.fixed
@@ -0,0 +1,140 @@
+// run-rustfix
+#![feature(lint_reasons)]
+#![warn(clippy::implicit_return)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ return true
+}
+
+fn test_if_block() -> bool {
+ if true { return true } else { return false }
+}
+
+#[rustfmt::skip]
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => { return true },
+ }
+}
+
+fn test_match_with_unreachable(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => unreachable!(),
+ }
+}
+
+fn test_loop() -> bool {
+ loop {
+ return true;
+ }
+}
+
+fn test_loop_with_block() -> bool {
+ loop {
+ {
+ return true;
+ }
+ }
+}
+
+fn test_loop_with_nests() -> bool {
+ loop {
+ if true {
+ return true;
+ } else {
+ let _ = true;
+ }
+ }
+}
+
+#[allow(clippy::redundant_pattern_matching)]
+fn test_loop_with_if_let() -> bool {
+ loop {
+ if let Some(x) = Some(true) {
+ return x;
+ }
+ }
+}
+
+fn test_closure() {
+ #[rustfmt::skip]
+ let _ = || { return true };
+ let _ = || return true;
+}
+
+fn test_panic() -> bool {
+ panic!()
+}
+
+fn test_return_macro() -> String {
+ return format!("test {}", "test")
+}
+
+fn macro_branch_test() -> bool {
+ macro_rules! m {
+ ($t:expr, $f:expr) => {
+ if true { $t } else { $f }
+ };
+ }
+ return m!(true, false)
+}
+
+fn loop_test() -> bool {
+ 'outer: loop {
+ if true {
+ return true;
+ }
+
+ let _ = loop {
+ if false {
+ return false;
+ }
+ if true {
+ break true;
+ }
+ };
+ }
+}
+
+fn loop_macro_test() -> bool {
+ macro_rules! m {
+ ($e:expr) => {
+ break $e
+ };
+ }
+ return loop {
+ m!(true);
+ }
+}
+
+fn divergent_test() -> bool {
+ fn diverge() -> ! {
+ panic!()
+ }
+ diverge()
+}
+
+// issue #6940
+async fn foo() -> bool {
+ return true
+}
+
+fn main() {}
+
+fn check_expect() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ #[expect(clippy::implicit_return)]
+ true
+}
diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs
new file mode 100644
index 000000000..76f0a9803
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_return.rs
@@ -0,0 +1,140 @@
+// run-rustfix
+#![feature(lint_reasons)]
+#![warn(clippy::implicit_return)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ true
+}
+
+fn test_if_block() -> bool {
+ if true { true } else { false }
+}
+
+#[rustfmt::skip]
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => { true },
+ }
+}
+
+fn test_match_with_unreachable(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => unreachable!(),
+ }
+}
+
+fn test_loop() -> bool {
+ loop {
+ break true;
+ }
+}
+
+fn test_loop_with_block() -> bool {
+ loop {
+ {
+ break true;
+ }
+ }
+}
+
+fn test_loop_with_nests() -> bool {
+ loop {
+ if true {
+ break true;
+ } else {
+ let _ = true;
+ }
+ }
+}
+
+#[allow(clippy::redundant_pattern_matching)]
+fn test_loop_with_if_let() -> bool {
+ loop {
+ if let Some(x) = Some(true) {
+ return x;
+ }
+ }
+}
+
+fn test_closure() {
+ #[rustfmt::skip]
+ let _ = || { true };
+ let _ = || true;
+}
+
+fn test_panic() -> bool {
+ panic!()
+}
+
+fn test_return_macro() -> String {
+ format!("test {}", "test")
+}
+
+fn macro_branch_test() -> bool {
+ macro_rules! m {
+ ($t:expr, $f:expr) => {
+ if true { $t } else { $f }
+ };
+ }
+ m!(true, false)
+}
+
+fn loop_test() -> bool {
+ 'outer: loop {
+ if true {
+ break true;
+ }
+
+ let _ = loop {
+ if false {
+ break 'outer false;
+ }
+ if true {
+ break true;
+ }
+ };
+ }
+}
+
+fn loop_macro_test() -> bool {
+ macro_rules! m {
+ ($e:expr) => {
+ break $e
+ };
+ }
+ loop {
+ m!(true);
+ }
+}
+
+fn divergent_test() -> bool {
+ fn diverge() -> ! {
+ panic!()
+ }
+ diverge()
+}
+
+// issue #6940
+async fn foo() -> bool {
+ true
+}
+
+fn main() {}
+
+fn check_expect() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+
+ #[expect(clippy::implicit_return)]
+ true
+}
diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr
new file mode 100644
index 000000000..522bc3bf8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_return.stderr
@@ -0,0 +1,109 @@
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:12:5
+ |
+LL | true
+ | ^^^^ help: add `return` as shown: `return true`
+ |
+ = note: `-D clippy::implicit-return` implied by `-D warnings`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:16:15
+ |
+LL | if true { true } else { false }
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:16:29
+ |
+LL | if true { true } else { false }
+ | ^^^^^ help: add `return` as shown: `return false`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:22:17
+ |
+LL | true => false,
+ | ^^^^^ help: add `return` as shown: `return false`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:23:20
+ |
+LL | false => { true },
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:36:9
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:43:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:51:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:69:18
+ |
+LL | let _ = || { true };
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:70:16
+ |
+LL | let _ = || true;
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:78:5
+ |
+LL | format!("test {}", "test")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:87:5
+ |
+LL | m!(true, false)
+ | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:93:13
+ |
+LL | break true;
+ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:98:17
+ |
+LL | break 'outer false;
+ | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false`
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:113:5
+ |
+LL | / loop {
+LL | | m!(true);
+LL | | }
+ | |_____^
+ |
+help: add `return` as shown
+ |
+LL ~ return loop {
+LL + m!(true);
+LL + }
+ |
+
+error: missing `return` statement
+ --> $DIR/implicit_return.rs:127:5
+ |
+LL | true
+ | ^^^^ help: add `return` as shown: `return true`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
new file mode 100644
index 000000000..e6f57e926
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
@@ -0,0 +1,168 @@
+// run-rustfix
+#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
+#![warn(clippy::implicit_saturating_sub)]
+
+fn main() {
+ // Tests for unsigned integers
+
+ let end_8: u8 = 10;
+ let start_8: u8 = 5;
+ let mut u_8: u8 = end_8 - start_8;
+
+ // Lint
+ u_8 = u_8.saturating_sub(1);
+
+ match end_8 {
+ 10 => {
+ // Lint
+ u_8 = u_8.saturating_sub(1);
+ },
+ 11 => u_8 += 1,
+ _ => u_8 = 0,
+ }
+
+ let end_16: u16 = 40;
+ let start_16: u16 = 35;
+
+ let mut u_16: u16 = end_16 - start_16;
+
+ // Lint
+ u_16 = u_16.saturating_sub(1);
+
+ let mut end_32: u32 = 7010;
+ let mut start_32: u32 = 7000;
+
+ let mut u_32: u32 = end_32 - start_32;
+
+ // Lint
+ u_32 = u_32.saturating_sub(1);
+
+ // No Lint
+ if u_32 > 0 {
+ u_16 += 1;
+ }
+
+ // No Lint
+ if u_32 != 0 {
+ end_32 -= 1;
+ start_32 += 1;
+ }
+
+ let mut end_64: u64 = 75001;
+ let mut start_64: u64 = 75000;
+
+ let mut u_64: u64 = end_64 - start_64;
+
+ // Lint
+ u_64 = u_64.saturating_sub(1);
+
+ // Lint
+ u_64 = u_64.saturating_sub(1);
+
+ // Lint
+ u_64 = u_64.saturating_sub(1);
+
+ // No Lint
+ if u_64 >= 1 {
+ u_64 -= 1;
+ }
+
+ // No Lint
+ if u_64 > 0 {
+ end_64 -= 1;
+ }
+
+ // Tests for usize
+ let end_usize: usize = 8054;
+ let start_usize: usize = 8050;
+
+ let mut u_usize: usize = end_usize - start_usize;
+
+ // Lint
+ u_usize = u_usize.saturating_sub(1);
+
+ // Tests for signed integers
+
+ let endi_8: i8 = 10;
+ let starti_8: i8 = 50;
+
+ let mut i_8: i8 = endi_8 - starti_8;
+
+ // Lint
+ i_8 = i_8.saturating_sub(1);
+
+ // Lint
+ i_8 = i_8.saturating_sub(1);
+
+ // Lint
+ i_8 = i_8.saturating_sub(1);
+
+ // Lint
+ i_8 = i_8.saturating_sub(1);
+
+ let endi_16: i16 = 45;
+ let starti_16: i16 = 44;
+
+ let mut i_16: i16 = endi_16 - starti_16;
+
+ // Lint
+ i_16 = i_16.saturating_sub(1);
+
+ // Lint
+ i_16 = i_16.saturating_sub(1);
+
+ // Lint
+ i_16 = i_16.saturating_sub(1);
+
+ // Lint
+ i_16 = i_16.saturating_sub(1);
+
+ let endi_32: i32 = 45;
+ let starti_32: i32 = 44;
+
+ let mut i_32: i32 = endi_32 - starti_32;
+
+ // Lint
+ i_32 = i_32.saturating_sub(1);
+
+ // Lint
+ i_32 = i_32.saturating_sub(1);
+
+ // Lint
+ i_32 = i_32.saturating_sub(1);
+
+ // Lint
+ i_32 = i_32.saturating_sub(1);
+
+ let endi_64: i64 = 45;
+ let starti_64: i64 = 44;
+
+ let mut i_64: i64 = endi_64 - starti_64;
+
+ // Lint
+ i_64 = i_64.saturating_sub(1);
+
+ // Lint
+ i_64 = i_64.saturating_sub(1);
+
+ // Lint
+ i_64 = i_64.saturating_sub(1);
+
+ // No Lint
+ if i_64 > 0 {
+ i_64 -= 1;
+ }
+
+ // No Lint
+ if i_64 != 0 {
+ i_64 -= 1;
+ }
+
+ // issue #7831
+ // No Lint
+ if u_32 > 0 {
+ u_32 -= 1;
+ } else {
+ println!("side effect");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
new file mode 100644
index 000000000..8bb28d149
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
@@ -0,0 +1,214 @@
+// run-rustfix
+#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
+#![warn(clippy::implicit_saturating_sub)]
+
+fn main() {
+ // Tests for unsigned integers
+
+ let end_8: u8 = 10;
+ let start_8: u8 = 5;
+ let mut u_8: u8 = end_8 - start_8;
+
+ // Lint
+ if u_8 > 0 {
+ u_8 = u_8 - 1;
+ }
+
+ match end_8 {
+ 10 => {
+ // Lint
+ if u_8 > 0 {
+ u_8 -= 1;
+ }
+ },
+ 11 => u_8 += 1,
+ _ => u_8 = 0,
+ }
+
+ let end_16: u16 = 40;
+ let start_16: u16 = 35;
+
+ let mut u_16: u16 = end_16 - start_16;
+
+ // Lint
+ if u_16 > 0 {
+ u_16 -= 1;
+ }
+
+ let mut end_32: u32 = 7010;
+ let mut start_32: u32 = 7000;
+
+ let mut u_32: u32 = end_32 - start_32;
+
+ // Lint
+ if u_32 != 0 {
+ u_32 -= 1;
+ }
+
+ // No Lint
+ if u_32 > 0 {
+ u_16 += 1;
+ }
+
+ // No Lint
+ if u_32 != 0 {
+ end_32 -= 1;
+ start_32 += 1;
+ }
+
+ let mut end_64: u64 = 75001;
+ let mut start_64: u64 = 75000;
+
+ let mut u_64: u64 = end_64 - start_64;
+
+ // Lint
+ if u_64 > 0 {
+ u_64 -= 1;
+ }
+
+ // Lint
+ if 0 < u_64 {
+ u_64 -= 1;
+ }
+
+ // Lint
+ if 0 != u_64 {
+ u_64 -= 1;
+ }
+
+ // No Lint
+ if u_64 >= 1 {
+ u_64 -= 1;
+ }
+
+ // No Lint
+ if u_64 > 0 {
+ end_64 -= 1;
+ }
+
+ // Tests for usize
+ let end_usize: usize = 8054;
+ let start_usize: usize = 8050;
+
+ let mut u_usize: usize = end_usize - start_usize;
+
+ // Lint
+ if u_usize > 0 {
+ u_usize -= 1;
+ }
+
+ // Tests for signed integers
+
+ let endi_8: i8 = 10;
+ let starti_8: i8 = 50;
+
+ let mut i_8: i8 = endi_8 - starti_8;
+
+ // Lint
+ if i_8 > i8::MIN {
+ i_8 -= 1;
+ }
+
+ // Lint
+ if i_8 > i8::MIN {
+ i_8 -= 1;
+ }
+
+ // Lint
+ if i_8 != i8::MIN {
+ i_8 -= 1;
+ }
+
+ // Lint
+ if i_8 != i8::MIN {
+ i_8 -= 1;
+ }
+
+ let endi_16: i16 = 45;
+ let starti_16: i16 = 44;
+
+ let mut i_16: i16 = endi_16 - starti_16;
+
+ // Lint
+ if i_16 > i16::MIN {
+ i_16 -= 1;
+ }
+
+ // Lint
+ if i_16 > i16::MIN {
+ i_16 -= 1;
+ }
+
+ // Lint
+ if i_16 != i16::MIN {
+ i_16 -= 1;
+ }
+
+ // Lint
+ if i_16 != i16::MIN {
+ i_16 -= 1;
+ }
+
+ let endi_32: i32 = 45;
+ let starti_32: i32 = 44;
+
+ let mut i_32: i32 = endi_32 - starti_32;
+
+ // Lint
+ if i_32 > i32::MIN {
+ i_32 -= 1;
+ }
+
+ // Lint
+ if i_32 > i32::MIN {
+ i_32 -= 1;
+ }
+
+ // Lint
+ if i_32 != i32::MIN {
+ i_32 -= 1;
+ }
+
+ // Lint
+ if i_32 != i32::MIN {
+ i_32 -= 1;
+ }
+
+ let endi_64: i64 = 45;
+ let starti_64: i64 = 44;
+
+ let mut i_64: i64 = endi_64 - starti_64;
+
+ // Lint
+ if i64::MIN < i_64 {
+ i_64 -= 1;
+ }
+
+ // Lint
+ if i64::MIN != i_64 {
+ i_64 -= 1;
+ }
+
+ // Lint
+ if i64::MIN < i_64 {
+ i_64 -= 1;
+ }
+
+ // No Lint
+ if i_64 > 0 {
+ i_64 -= 1;
+ }
+
+ // No Lint
+ if i_64 != 0 {
+ i_64 -= 1;
+ }
+
+ // issue #7831
+ // No Lint
+ if u_32 > 0 {
+ u_32 -= 1;
+ } else {
+ println!("side effect");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
new file mode 100644
index 000000000..5bb9a6064
--- /dev/null
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
@@ -0,0 +1,188 @@
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:13:5
+ |
+LL | / if u_8 > 0 {
+LL | | u_8 = u_8 - 1;
+LL | | }
+ | |_____^ help: try: `u_8 = u_8.saturating_sub(1);`
+ |
+ = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:20:13
+ |
+LL | / if u_8 > 0 {
+LL | | u_8 -= 1;
+LL | | }
+ | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:34:5
+ |
+LL | / if u_16 > 0 {
+LL | | u_16 -= 1;
+LL | | }
+ | |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:44:5
+ |
+LL | / if u_32 != 0 {
+LL | | u_32 -= 1;
+LL | | }
+ | |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:65:5
+ |
+LL | / if u_64 > 0 {
+LL | | u_64 -= 1;
+LL | | }
+ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:70:5
+ |
+LL | / if 0 < u_64 {
+LL | | u_64 -= 1;
+LL | | }
+ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:75:5
+ |
+LL | / if 0 != u_64 {
+LL | | u_64 -= 1;
+LL | | }
+ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:96:5
+ |
+LL | / if u_usize > 0 {
+LL | | u_usize -= 1;
+LL | | }
+ | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:108:5
+ |
+LL | / if i_8 > i8::MIN {
+LL | | i_8 -= 1;
+LL | | }
+ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:113:5
+ |
+LL | / if i_8 > i8::MIN {
+LL | | i_8 -= 1;
+LL | | }
+ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:118:5
+ |
+LL | / if i_8 != i8::MIN {
+LL | | i_8 -= 1;
+LL | | }
+ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:123:5
+ |
+LL | / if i_8 != i8::MIN {
+LL | | i_8 -= 1;
+LL | | }
+ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:133:5
+ |
+LL | / if i_16 > i16::MIN {
+LL | | i_16 -= 1;
+LL | | }
+ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:138:5
+ |
+LL | / if i_16 > i16::MIN {
+LL | | i_16 -= 1;
+LL | | }
+ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:143:5
+ |
+LL | / if i_16 != i16::MIN {
+LL | | i_16 -= 1;
+LL | | }
+ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:148:5
+ |
+LL | / if i_16 != i16::MIN {
+LL | | i_16 -= 1;
+LL | | }
+ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:158:5
+ |
+LL | / if i_32 > i32::MIN {
+LL | | i_32 -= 1;
+LL | | }
+ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:163:5
+ |
+LL | / if i_32 > i32::MIN {
+LL | | i_32 -= 1;
+LL | | }
+ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:168:5
+ |
+LL | / if i_32 != i32::MIN {
+LL | | i_32 -= 1;
+LL | | }
+ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:173:5
+ |
+LL | / if i_32 != i32::MIN {
+LL | | i_32 -= 1;
+LL | | }
+ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:183:5
+ |
+LL | / if i64::MIN < i_64 {
+LL | | i_64 -= 1;
+LL | | }
+ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:188:5
+ |
+LL | / if i64::MIN != i_64 {
+LL | | i_64 -= 1;
+LL | | }
+ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
+
+error: implicitly performing saturating subtraction
+ --> $DIR/implicit_saturating_sub.rs:193:5
+ |
+LL | / if i64::MIN < i_64 {
+LL | | i_64 -= 1;
+LL | | }
+ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed
new file mode 100644
index 000000000..dd683e7f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+#[warn(clippy::inconsistent_digit_grouping)]
+#[deny(clippy::unreadable_literal)]
+#[allow(unused_variables, clippy::excessive_precision)]
+fn main() {
+ macro_rules! mac1 {
+ () => {
+ 1_23_456
+ };
+ }
+ macro_rules! mac2 {
+ () => {
+ 1_234.5678_f32
+ };
+ }
+
+ let good = (
+ 123,
+ 1_234,
+ 1_2345_6789,
+ 123_f32,
+ 1_234.12_f32,
+ 1_234.123_4_f32,
+ 1.123_456_7_f32,
+ );
+ let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32);
+
+ // Test padding
+ let _ = 0x0010_0000;
+ let _ = 0x0100_0000;
+ let _ = 0x1000_0000;
+ let _ = 0x0001_0000_0000_u64;
+
+ // Test suggestion when fraction has no digits
+ let _: f32 = 123_456.;
+
+ // Test UUID formatted literal
+ let _: u128 = 0x12345678_1234_1234_1234_123456789012;
+
+ // Ignore literals in macros
+ let _ = mac1!();
+ let _ = mac2!();
+
+ // Issue #6096
+ // Allow separating exponent with '_'
+ let _ = 1.025_011_10_E0;
+}
diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs
new file mode 100644
index 000000000..d5d27c853
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs
@@ -0,0 +1,47 @@
+// run-rustfix
+#[warn(clippy::inconsistent_digit_grouping)]
+#[deny(clippy::unreadable_literal)]
+#[allow(unused_variables, clippy::excessive_precision)]
+fn main() {
+ macro_rules! mac1 {
+ () => {
+ 1_23_456
+ };
+ }
+ macro_rules! mac2 {
+ () => {
+ 1_234.5678_f32
+ };
+ }
+
+ let good = (
+ 123,
+ 1_234,
+ 1_2345_6789,
+ 123_f32,
+ 1_234.12_f32,
+ 1_234.123_4_f32,
+ 1.123_456_7_f32,
+ );
+ let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+
+ // Test padding
+ let _ = 0x100000;
+ let _ = 0x1000000;
+ let _ = 0x10000000;
+ let _ = 0x100000000_u64;
+
+ // Test suggestion when fraction has no digits
+ let _: f32 = 1_23_456.;
+
+ // Test UUID formatted literal
+ let _: u128 = 0x12345678_1234_1234_1234_123456789012;
+
+ // Ignore literals in macros
+ let _ = mac1!();
+ let _ = mac2!();
+
+ // Issue #6096
+ // Allow separating exponent with '_'
+ let _ = 1.025_011_10_E0;
+}
diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr
new file mode 100644
index 000000000..b8ac91554
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr
@@ -0,0 +1,70 @@
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:26:16
+ |
+LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+ | ^^^^^^^^ help: consider: `123_456`
+ |
+ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:26:26
+ |
+LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+ | ^^^^^^^^^^ help: consider: `12_345_678`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:26:38
+ |
+LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+ | ^^^^^^^^ help: consider: `1_234_567`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:26:48
+ |
+LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+ | ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:26:64
+ |
+LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
+ | ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32`
+
+error: long literal lacking separators
+ --> $DIR/inconsistent_digit_grouping.rs:29:13
+ |
+LL | let _ = 0x100000;
+ | ^^^^^^^^ help: consider: `0x0010_0000`
+ |
+note: the lint level is defined here
+ --> $DIR/inconsistent_digit_grouping.rs:3:8
+ |
+LL | #[deny(clippy::unreadable_literal)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: long literal lacking separators
+ --> $DIR/inconsistent_digit_grouping.rs:30:13
+ |
+LL | let _ = 0x1000000;
+ | ^^^^^^^^^ help: consider: `0x0100_0000`
+
+error: long literal lacking separators
+ --> $DIR/inconsistent_digit_grouping.rs:31:13
+ |
+LL | let _ = 0x10000000;
+ | ^^^^^^^^^^ help: consider: `0x1000_0000`
+
+error: long literal lacking separators
+ --> $DIR/inconsistent_digit_grouping.rs:32:13
+ |
+LL | let _ = 0x100000000_u64;
+ | ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/inconsistent_digit_grouping.rs:35:18
+ |
+LL | let _: f32 = 1_23_456.;
+ | ^^^^^^^^^ help: consider: `123_456.`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
new file mode 100644
index 000000000..74ba2f1c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
@@ -0,0 +1,73 @@
+// run-rustfix
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
+mod without_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { x, y, z };
+
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
+ // Should NOT lint because the order is the same as in the definition.
+ Foo { x, y, z };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo { y, x, z: z };
+ }
+}
+
+mod with_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { x, z, ..Default::default() };
+
+ // Should NOT lint because the order is consistent with the definition.
+ Foo {
+ x,
+ z,
+ ..Default::default()
+ };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo {
+ z: z,
+ x,
+ ..Default::default()
+ };
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
new file mode 100644
index 000000000..ba96e1e33
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
@@ -0,0 +1,77 @@
+// run-rustfix
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+macro_rules! new_foo {
+ () => {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ };
+}
+
+mod without_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let y = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo { y, x, z };
+
+ // Should NOT lint.
+ // issue #7069.
+ new_foo!();
+
+ // Should NOT lint because the order is the same as in the definition.
+ Foo { x, y, z };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo { y, x, z: z };
+ }
+}
+
+mod with_base {
+ use super::Foo;
+
+ fn test() {
+ let x = 1;
+ let z = 1;
+
+ // Should lint.
+ Foo {
+ z,
+ x,
+ ..Default::default()
+ };
+
+ // Should NOT lint because the order is consistent with the definition.
+ Foo {
+ x,
+ z,
+ ..Default::default()
+ };
+
+ // Should NOT lint because z is not a shorthand init.
+ Foo {
+ z: z,
+ x,
+ ..Default::default()
+ };
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
new file mode 100644
index 000000000..c90189e96
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
@@ -0,0 +1,20 @@
+error: struct constructor field order is inconsistent with struct definition field order
+ --> $DIR/inconsistent_struct_constructor.rs:33:9
+ |
+LL | Foo { y, x, z };
+ | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
+ |
+ = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
+
+error: struct constructor field order is inconsistent with struct definition field order
+ --> $DIR/inconsistent_struct_constructor.rs:55:9
+ |
+LL | / Foo {
+LL | | z,
+LL | | x,
+LL | | ..Default::default()
+LL | | };
+ | |_________^ help: try: `Foo { x, z, ..Default::default() }`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs
new file mode 100644
index 000000000..c2c0c520d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs
@@ -0,0 +1,166 @@
+#![deny(clippy::index_refutable_slice)]
+
+enum SomeEnum<T> {
+ One(T),
+ Two(T),
+ Three(T),
+ Four(T),
+}
+
+fn lintable_examples() {
+ // Try with reference
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+
+ // Try with copy
+ let slice: Option<[u32; 3]> = Some([1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+
+ // Try with long slice and small indices
+ let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ if let Some(slice) = slice {
+ println!("{}", slice[2]);
+ println!("{}", slice[0]);
+ }
+
+ // Multiple bindings
+ let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]);
+ if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+ println!("{}", slice[0]);
+ }
+
+ // Two lintable slices in one if let
+ let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]);
+ let b_wrapped: Option<[u32; 2]> = Some([4, 6]);
+ if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ println!("{} -> {}", a[2], b[1]);
+ }
+
+ // This requires the slice values to be borrowed as the slice values can only be
+ // borrowed and `String` doesn't implement copy
+ let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+ if let Some(ref slice) = slice {
+ println!("{:?}", slice[1]);
+ }
+ println!("{:?}", slice);
+
+ // This should not suggest using the `ref` keyword as the scrutinee is already
+ // a reference
+ let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+ if let Some(slice) = &slice {
+ println!("{:?}", slice[0]);
+ }
+ println!("{:?}", slice);
+}
+
+fn slice_index_above_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+
+ if let Some(slice) = slice {
+ // Would cause a panic, IDK
+ println!("{}", slice[7]);
+ }
+}
+
+fn slice_is_used() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{:?}", slice.len());
+ }
+
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{:?}", slice.to_vec());
+ }
+
+ let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+ if let Some(slice) = opt {
+ if !slice.is_empty() {
+ println!("first: {}", slice[0]);
+ }
+ }
+}
+
+/// The slice is used by an external function and should therefore not be linted
+fn check_slice_as_arg() {
+ fn is_interesting<T>(slice: &[T; 2]) -> bool {
+ !slice.is_empty()
+ }
+
+ let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+ if let Some(slice) = &slice_wrapped {
+ if is_interesting(slice) {
+ println!("This is interesting {}", slice[0]);
+ }
+ }
+ println!("{:?}", slice_wrapped);
+}
+
+fn check_slice_in_struct() {
+ #[derive(Debug)]
+ struct Wrapper<'a> {
+ inner: Option<&'a [String]>,
+ is_awesome: bool,
+ }
+
+ impl<'a> Wrapper<'a> {
+ fn is_super_awesome(&self) -> bool {
+ self.is_awesome
+ }
+ }
+
+ let inner = &[String::from("New"), String::from("World")];
+ let wrap = Wrapper {
+ inner: Some(inner),
+ is_awesome: true,
+ };
+
+ // Test 1: Field access
+ if let Some(slice) = wrap.inner {
+ if wrap.is_awesome {
+ println!("This is awesome! {}", slice[0]);
+ }
+ }
+
+ // Test 2: function access
+ if let Some(slice) = wrap.inner {
+ if wrap.is_super_awesome() {
+ println!("This is super awesome! {}", slice[0]);
+ }
+ }
+ println!("Complete wrap: {:?}", wrap);
+}
+
+/// This would be a nice additional feature to have in the future, but adding it
+/// now would make the PR too large. This is therefore only a test that we don't
+/// lint cases we can't make a reasonable suggestion for
+fn mutable_slice_index() {
+ // Mut access
+ let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]);
+ if let Some(ref mut slice) = slice {
+ slice[0] = String::from("Mr. Penguin");
+ }
+ println!("Use after modification: {:?}", slice);
+
+ // Mut access on reference
+ let mut slice: Option<[String; 1]> = Some([String::from("Cat")]);
+ if let Some(slice) = &mut slice {
+ slice[0] = String::from("Lord Meow Meow");
+ }
+ println!("Use after modification: {:?}", slice);
+}
+
+/// The lint will ignore bindings with sub patterns as it would be hard
+/// to build correct suggestions for these instances :)
+fn binding_with_sub_pattern() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice @ [_, _, _]) = slice {
+ println!("{:?}", slice[2]);
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr
new file mode 100644
index 000000000..a607df9b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr
@@ -0,0 +1,158 @@
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:13:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/if_let_slice_binding.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:19:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:25:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, _, slice_2, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL ~ println!("{}", slice_2);
+LL ~ println!("{}", slice_0);
+ |
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:32:26
+ |
+LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped {
+ | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:39:29
+ |
+LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ | ^
+ |
+help: try using a slice pattern here
+ |
+LL | if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) {
+ | ~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{} -> {}", a_2, b[1]);
+ | ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:39:38
+ |
+LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ | ^
+ |
+help: try using a slice pattern here
+ |
+LL | if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) {
+ | ~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{} -> {}", a[2], b_1);
+ | ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:46:21
+ |
+LL | if let Some(ref slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([_, ref slice_1, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{:?}", slice_1);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:54:17
+ |
+LL | if let Some(slice) = &slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = &slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{:?}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:123:17
+ |
+LL | if let Some(slice) = wrap.inner {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = wrap.inner {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("This is awesome! {}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:130:17
+ |
+LL | if let Some(slice) = wrap.inner {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = wrap.inner {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("This is super awesome! {}", slice_0);
+ | ~~~~~~~
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs
new file mode 100644
index 000000000..406e82083
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs
@@ -0,0 +1,28 @@
+#![deny(clippy::index_refutable_slice)]
+
+extern crate if_chain;
+use if_chain::if_chain;
+
+macro_rules! if_let_slice_macro {
+ () => {
+ // This would normally be linted
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+ };
+}
+
+fn main() {
+ // Don't lint this
+ if_let_slice_macro!();
+
+ // Do lint this
+ if_chain! {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice;
+ then {
+ println!("{}", slice[0]);
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr
new file mode 100644
index 000000000..11b19428b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/slice_indexing_in_macro.rs:23:21
+ |
+LL | if let Some(slice) = slice;
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/slice_indexing_in_macro.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice;
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
new file mode 100644
index 000000000..45a430edc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs
@@ -0,0 +1,48 @@
+#![feature(inline_const)]
+#![warn(clippy::indexing_slicing)]
+// We also check the out_of_bounds_indexing lint here, because it lints similar things and
+// we want to avoid false positives.
+#![warn(clippy::out_of_bounds_indexing)]
+#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)]
+
+const ARR: [i32; 2] = [1, 2];
+const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
+const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+
+const fn idx() -> usize {
+ 1
+}
+const fn idx4() -> usize {
+ 4
+}
+
+fn main() {
+ let x = [1, 2, 3, 4];
+ let index: usize = 1;
+ x[index];
+ x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+
+ x[0]; // Ok, should not produce stderr.
+ x[3]; // Ok, should not produce stderr.
+ x[const { idx() }]; // Ok, should not produce stderr.
+ x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ const { &ARR[idx()] }; // Ok, should not produce stderr.
+ const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+
+ let y = &x;
+ y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
+ y[4]; // Ok, rustc will handle references too.
+
+ let v = vec![0; 5];
+ v[0];
+ v[10];
+ v[1 << 3];
+
+ const N: usize = 15; // Out of bounds
+ const M: usize = 3; // In bounds
+ x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ x[M]; // Ok, should not produce stderr.
+ v[N];
+ v[M];
+}
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
new file mode 100644
index 000000000..6ae700753
--- /dev/null
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -0,0 +1,64 @@
+error[E0080]: evaluation of `main::{constant#3}` failed
+ --> $DIR/indexing_slicing_index.rs:31:14
+ |
+LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
+
+error[E0080]: erroneous constant used
+ --> $DIR/indexing_slicing_index.rs:31:5
+ |
+LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:22:5
+ |
+LL | x[index];
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:38:5
+ |
+LL | v[0];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:39:5
+ |
+LL | v[10];
+ | ^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:40:5
+ |
+LL | v[1 << 3];
+ | ^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:46:5
+ |
+LL | v[N];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:47:5
+ |
+LL | v[M];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs
new file mode 100644
index 000000000..7b107db39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs
@@ -0,0 +1,37 @@
+#![warn(clippy::indexing_slicing)]
+// We also check the out_of_bounds_indexing lint here, because it lints similar things and
+// we want to avoid false positives.
+#![warn(clippy::out_of_bounds_indexing)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation)]
+
+fn main() {
+ let x = [1, 2, 3, 4];
+ let index: usize = 1;
+ let index_from: usize = 2;
+ let index_to: usize = 3;
+ &x[index..];
+ &x[..index];
+ &x[index_from..index_to];
+ &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
+ &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
+ &x[0..][..3];
+ &x[1..][..5];
+
+ &x[0..].get(..3); // Ok, should not produce stderr.
+ &x[0..3]; // Ok, should not produce stderr.
+
+ let y = &x;
+ &y[1..2];
+ &y[0..=4];
+ &y[..=4];
+
+ &y[..]; // Ok, should not produce stderr.
+
+ let v = vec![0; 5];
+ &v[10..100];
+ &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
+ &v[10..];
+ &v[..100];
+
+ &v[..]; // Ok, should not produce stderr.
+}
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr
new file mode 100644
index 000000000..f70722b92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr
@@ -0,0 +1,125 @@
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:12:6
+ |
+LL | &x[index..];
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
+ = help: consider using `.get(n..)` or .get_mut(n..)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:13:6
+ |
+LL | &x[..index];
+ | ^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:14:6
+ |
+LL | &x[index_from..index_to];
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:15:6
+ |
+LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:15:6
+ |
+LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider using `.get(n..)` or .get_mut(n..)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:16:6
+ |
+LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
+ | ^^^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: range is out of bounds
+ --> $DIR/indexing_slicing_slice.rs:16:8
+ |
+LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
+ | ^
+ |
+ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:17:6
+ |
+LL | &x[0..][..3];
+ | ^^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:18:6
+ |
+LL | &x[1..][..5];
+ | ^^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: range is out of bounds
+ --> $DIR/indexing_slicing_slice.rs:25:12
+ |
+LL | &y[0..=4];
+ | ^
+
+error: range is out of bounds
+ --> $DIR/indexing_slicing_slice.rs:26:11
+ |
+LL | &y[..=4];
+ | ^
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:31:6
+ |
+LL | &v[10..100];
+ | ^^^^^^^^^^
+ |
+ = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:32:6
+ |
+LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: range is out of bounds
+ --> $DIR/indexing_slicing_slice.rs:32:8
+ |
+LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
+ | ^^
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:33:6
+ |
+LL | &v[10..];
+ | ^^^^^^^
+ |
+ = help: consider using `.get(n..)` or .get_mut(n..)` instead
+
+error: slicing may panic
+ --> $DIR/indexing_slicing_slice.rs:34:6
+ |
+LL | &v[..100];
+ | ^^^^^^^^
+ |
+ = help: consider using `.get(..n)`or `.get_mut(..n)` instead
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.fixed b/src/tools/clippy/tests/ui/inefficient_to_string.fixed
new file mode 100644
index 000000000..c972b9419
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inefficient_to_string.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+#![deny(clippy::inefficient_to_string)]
+
+use std::borrow::Cow;
+
+fn main() {
+ let rstr: &str = "hello";
+ let rrstr: &&str = &rstr;
+ let rrrstr: &&&str = &rrstr;
+ let _: String = rstr.to_string();
+ let _: String = (*rrstr).to_string();
+ let _: String = (**rrrstr).to_string();
+
+ let string: String = String::from("hello");
+ let rstring: &String = &string;
+ let rrstring: &&String = &rstring;
+ let rrrstring: &&&String = &rrstring;
+ let _: String = string.to_string();
+ let _: String = rstring.to_string();
+ let _: String = (*rrstring).to_string();
+ let _: String = (**rrrstring).to_string();
+
+ let cow: Cow<'_, str> = Cow::Borrowed("hello");
+ let rcow: &Cow<'_, str> = &cow;
+ let rrcow: &&Cow<'_, str> = &rcow;
+ let rrrcow: &&&Cow<'_, str> = &rrcow;
+ let _: String = cow.to_string();
+ let _: String = rcow.to_string();
+ let _: String = (*rrcow).to_string();
+ let _: String = (**rrrcow).to_string();
+}
diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.rs b/src/tools/clippy/tests/ui/inefficient_to_string.rs
new file mode 100644
index 000000000..acdc55aa0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inefficient_to_string.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+#![deny(clippy::inefficient_to_string)]
+
+use std::borrow::Cow;
+
+fn main() {
+ let rstr: &str = "hello";
+ let rrstr: &&str = &rstr;
+ let rrrstr: &&&str = &rrstr;
+ let _: String = rstr.to_string();
+ let _: String = rrstr.to_string();
+ let _: String = rrrstr.to_string();
+
+ let string: String = String::from("hello");
+ let rstring: &String = &string;
+ let rrstring: &&String = &rstring;
+ let rrrstring: &&&String = &rrstring;
+ let _: String = string.to_string();
+ let _: String = rstring.to_string();
+ let _: String = rrstring.to_string();
+ let _: String = rrrstring.to_string();
+
+ let cow: Cow<'_, str> = Cow::Borrowed("hello");
+ let rcow: &Cow<'_, str> = &cow;
+ let rrcow: &&Cow<'_, str> = &rcow;
+ let rrrcow: &&&Cow<'_, str> = &rrcow;
+ let _: String = cow.to_string();
+ let _: String = rcow.to_string();
+ let _: String = rrcow.to_string();
+ let _: String = rrrcow.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.stderr b/src/tools/clippy/tests/ui/inefficient_to_string.stderr
new file mode 100644
index 000000000..4be46161e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inefficient_to_string.stderr
@@ -0,0 +1,55 @@
+error: calling `to_string` on `&&str`
+ --> $DIR/inefficient_to_string.rs:11:21
+ |
+LL | let _: String = rrstr.to_string();
+ | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()`
+ |
+note: the lint level is defined here
+ --> $DIR/inefficient_to_string.rs:2:9
+ |
+LL | #![deny(clippy::inefficient_to_string)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
+
+error: calling `to_string` on `&&&str`
+ --> $DIR/inefficient_to_string.rs:12:21
+ |
+LL | let _: String = rrrstr.to_string();
+ | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()`
+ |
+ = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
+
+error: calling `to_string` on `&&std::string::String`
+ --> $DIR/inefficient_to_string.rs:20:21
+ |
+LL | let _: String = rrstring.to_string();
+ | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()`
+ |
+ = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
+
+error: calling `to_string` on `&&&std::string::String`
+ --> $DIR/inefficient_to_string.rs:21:21
+ |
+LL | let _: String = rrrstring.to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()`
+ |
+ = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
+
+error: calling `to_string` on `&&std::borrow::Cow<str>`
+ --> $DIR/inefficient_to_string.rs:29:21
+ |
+LL | let _: String = rrcow.to_string();
+ | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()`
+ |
+ = help: `&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
+
+error: calling `to_string` on `&&&std::borrow::Cow<str>`
+ --> $DIR/inefficient_to_string.rs:30:21
+ |
+LL | let _: String = rrrcow.to_string();
+ | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()`
+ |
+ = help: `&&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
new file mode 100644
index 000000000..b8e40d995
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
@@ -0,0 +1,112 @@
+// run-rustfix
+#![feature(exhaustive_patterns, never_type)]
+#![allow(dead_code, unreachable_code, unused_variables)]
+#![allow(clippy::let_and_return)]
+
+enum SingleVariantEnum {
+ Variant(i32),
+}
+
+struct TupleStruct(i32);
+
+enum EmptyEnum {}
+
+macro_rules! match_enum {
+ ($param:expr) => {
+ let data = match $param {
+ SingleVariantEnum::Variant(i) => i,
+ };
+ };
+}
+
+fn infallible_destructuring_match_enum() {
+ let wrapper = SingleVariantEnum::Variant(0);
+
+ // This should lint!
+ let SingleVariantEnum::Variant(data) = wrapper;
+
+ // This shouldn't (inside macro)
+ match_enum!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ SingleVariantEnum::Variant(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ SingleVariantEnum::Variant(i) => -1,
+ };
+
+ let SingleVariantEnum::Variant(data) = wrapper;
+}
+
+macro_rules! match_struct {
+ ($param:expr) => {
+ let data = match $param {
+ TupleStruct(i) => i,
+ };
+ };
+}
+
+fn infallible_destructuring_match_struct() {
+ let wrapper = TupleStruct(0);
+
+ // This should lint!
+ let TupleStruct(data) = wrapper;
+
+ // This shouldn't (inside macro)
+ match_struct!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ TupleStruct(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ TupleStruct(i) => -1,
+ };
+
+ let TupleStruct(data) = wrapper;
+}
+
+macro_rules! match_never_enum {
+ ($param:expr) => {
+ let data = match $param {
+ Ok(i) => i,
+ };
+ };
+}
+
+fn never_enum() {
+ let wrapper: Result<i32, !> = Ok(23);
+
+ // This should lint!
+ let Ok(data) = wrapper;
+
+ // This shouldn't (inside macro)
+ match_never_enum!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ Ok(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ Ok(i) => -1,
+ };
+
+ let Ok(data) = wrapper;
+}
+
+impl EmptyEnum {
+ fn match_on(&self) -> ! {
+ // The lint shouldn't pick this up, as `let` won't work here!
+ let data = match *self {};
+ data
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs
new file mode 100644
index 000000000..106cd438b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs
@@ -0,0 +1,118 @@
+// run-rustfix
+#![feature(exhaustive_patterns, never_type)]
+#![allow(dead_code, unreachable_code, unused_variables)]
+#![allow(clippy::let_and_return)]
+
+enum SingleVariantEnum {
+ Variant(i32),
+}
+
+struct TupleStruct(i32);
+
+enum EmptyEnum {}
+
+macro_rules! match_enum {
+ ($param:expr) => {
+ let data = match $param {
+ SingleVariantEnum::Variant(i) => i,
+ };
+ };
+}
+
+fn infallible_destructuring_match_enum() {
+ let wrapper = SingleVariantEnum::Variant(0);
+
+ // This should lint!
+ let data = match wrapper {
+ SingleVariantEnum::Variant(i) => i,
+ };
+
+ // This shouldn't (inside macro)
+ match_enum!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ SingleVariantEnum::Variant(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ SingleVariantEnum::Variant(i) => -1,
+ };
+
+ let SingleVariantEnum::Variant(data) = wrapper;
+}
+
+macro_rules! match_struct {
+ ($param:expr) => {
+ let data = match $param {
+ TupleStruct(i) => i,
+ };
+ };
+}
+
+fn infallible_destructuring_match_struct() {
+ let wrapper = TupleStruct(0);
+
+ // This should lint!
+ let data = match wrapper {
+ TupleStruct(i) => i,
+ };
+
+ // This shouldn't (inside macro)
+ match_struct!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ TupleStruct(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ TupleStruct(i) => -1,
+ };
+
+ let TupleStruct(data) = wrapper;
+}
+
+macro_rules! match_never_enum {
+ ($param:expr) => {
+ let data = match $param {
+ Ok(i) => i,
+ };
+ };
+}
+
+fn never_enum() {
+ let wrapper: Result<i32, !> = Ok(23);
+
+ // This should lint!
+ let data = match wrapper {
+ Ok(i) => i,
+ };
+
+ // This shouldn't (inside macro)
+ match_never_enum!(wrapper);
+
+ // This shouldn't!
+ let data = match wrapper {
+ Ok(_) => -1,
+ };
+
+ // Neither should this!
+ let data = match wrapper {
+ Ok(i) => -1,
+ };
+
+ let Ok(data) = wrapper;
+}
+
+impl EmptyEnum {
+ fn match_on(&self) -> ! {
+ // The lint shouldn't pick this up, as `let` won't work here!
+ let data = match *self {};
+ data
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr
new file mode 100644
index 000000000..1b78db420
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr
@@ -0,0 +1,28 @@
+error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
+ --> $DIR/infallible_destructuring_match.rs:26:5
+ |
+LL | / let data = match wrapper {
+LL | | SingleVariantEnum::Variant(i) => i,
+LL | | };
+ | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;`
+ |
+ = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings`
+
+error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
+ --> $DIR/infallible_destructuring_match.rs:58:5
+ |
+LL | / let data = match wrapper {
+LL | | TupleStruct(i) => i,
+LL | | };
+ | |______^ help: try this: `let TupleStruct(data) = wrapper;`
+
+error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
+ --> $DIR/infallible_destructuring_match.rs:90:5
+ |
+LL | / let data = match wrapper {
+LL | | Ok(i) => i,
+LL | | };
+ | |______^ help: try this: `let Ok(data) = wrapper;`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs
new file mode 100644
index 000000000..a1e5fad0c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_iter.rs
@@ -0,0 +1,68 @@
+use std::iter::repeat;
+fn square_is_lower_64(x: &u32) -> bool {
+ x * x < 64
+}
+
+#[allow(clippy::maybe_infinite_iter)]
+#[deny(clippy::infinite_iter)]
+fn infinite_iters() {
+ repeat(0_u8).collect::<Vec<_>>(); // infinite iter
+ (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
+ (0..8_u64).chain(0..).max(); // infinite iter
+ (0_usize..)
+ .chain([0usize, 1, 2].iter().cloned())
+ .skip_while(|x| *x != 42)
+ .min(); // infinite iter
+ (0..8_u32)
+ .rev()
+ .cycle()
+ .map(|x| x + 1_u32)
+ .for_each(|x| println!("{}", x)); // infinite iter
+ (0..3_u32).flat_map(|x| x..).sum::<u32>(); // infinite iter
+ (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
+ (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
+ (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended
+ (0..).next(); // iterator is not exhausted
+}
+
+#[deny(clippy::maybe_infinite_iter)]
+fn potential_infinite_iters() {
+ (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
+ repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
+ (1..)
+ .scan(0, |state, x| {
+ *state += x;
+ Some(*state)
+ })
+ .min(); // maybe infinite iter
+ (0..).find(|x| *x == 24); // maybe infinite iter
+ (0..).position(|x| x == 24); // maybe infinite iter
+ (0..).any(|x| x == 24); // maybe infinite iter
+ (0..).all(|x| x == 24); // maybe infinite iter
+
+ (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite
+ repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted
+}
+
+fn main() {
+ infinite_iters();
+ potential_infinite_iters();
+}
+
+mod finite_collect {
+ use std::collections::HashSet;
+
+ struct C;
+ impl FromIterator<i32> for C {
+ fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self {
+ C
+ }
+ }
+
+ fn check_collect() {
+ let _: HashSet<i32> = (0..).collect(); // Infinite iter
+
+ // Some data structures don't collect infinitely, such as `ArrayVec`
+ let _: C = (0..).collect();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/infinite_iter.stderr b/src/tools/clippy/tests/ui/infinite_iter.stderr
new file mode 100644
index 000000000..ba277e363
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_iter.stderr
@@ -0,0 +1,109 @@
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:9:5
+ |
+LL | repeat(0_u8).collect::<Vec<_>>(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/infinite_iter.rs:7:8
+ |
+LL | #[deny(clippy::infinite_iter)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:10:5
+ |
+LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:11:5
+ |
+LL | (0..8_u64).chain(0..).max(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:16:5
+ |
+LL | / (0..8_u32)
+LL | | .rev()
+LL | | .cycle()
+LL | | .map(|x| x + 1_u32)
+LL | | .for_each(|x| println!("{}", x)); // infinite iter
+ | |________________________________________^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:22:5
+ |
+LL | (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:23:5
+ |
+LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:30:5
+ |
+LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/infinite_iter.rs:28:8
+ |
+LL | #[deny(clippy::maybe_infinite_iter)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:31:5
+ |
+LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:32:5
+ |
+LL | / (1..)
+LL | | .scan(0, |state, x| {
+LL | | *state += x;
+LL | | Some(*state)
+LL | | })
+LL | | .min(); // maybe infinite iter
+ | |______________^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:38:5
+ |
+LL | (0..).find(|x| *x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:39:5
+ |
+LL | (0..).position(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:40:5
+ |
+LL | (0..).any(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: possible infinite iteration detected
+ --> $DIR/infinite_iter.rs:41:5
+ |
+LL | (0..).all(|x| x == 24); // maybe infinite iter
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: infinite iteration detected
+ --> $DIR/infinite_iter.rs:63:31
+ |
+LL | let _: HashSet<i32> = (0..).collect(); // Infinite iter
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `#[deny(clippy::infinite_iter)]` on by default
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs
new file mode 100644
index 000000000..38e64b9ac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_loop.rs
@@ -0,0 +1,217 @@
+fn fn_val(i: i32) -> i32 {
+ unimplemented!()
+}
+fn fn_constref(i: &i32) -> i32 {
+ unimplemented!()
+}
+fn fn_mutref(i: &mut i32) {
+ unimplemented!()
+}
+fn fooi() -> i32 {
+ unimplemented!()
+}
+fn foob() -> bool {
+ unimplemented!()
+}
+
+fn immutable_condition() {
+ // Should warn when all vars mentioned are immutable
+ let y = 0;
+ while y < 10 {
+ println!("KO - y is immutable");
+ }
+
+ let x = 0;
+ while y < 10 && x < 3 {
+ let mut k = 1;
+ k += 2;
+ println!("KO - x and y immutable");
+ }
+
+ let cond = false;
+ while !cond {
+ println!("KO - cond immutable");
+ }
+
+ let mut i = 0;
+ while y < 10 && i < 3 {
+ i += 1;
+ println!("OK - i is mutable");
+ }
+
+ let mut mut_cond = false;
+ while !mut_cond || cond {
+ mut_cond = true;
+ println!("OK - mut_cond is mutable");
+ }
+
+ while fooi() < x {
+ println!("OK - Fn call results may vary");
+ }
+
+ while foob() {
+ println!("OK - Fn call results may vary");
+ }
+
+ let mut a = 0;
+ let mut c = move || {
+ while a < 5 {
+ a += 1;
+ println!("OK - a is mutable");
+ }
+ };
+ c();
+
+ let mut tup = (0, 0);
+ while tup.0 < 5 {
+ tup.0 += 1;
+ println!("OK - tup.0 gets mutated")
+ }
+}
+
+fn unused_var() {
+ // Should warn when a (mutable) var is not used in while body
+ let (mut i, mut j) = (0, 0);
+
+ while i < 3 {
+ j = 3;
+ println!("KO - i not mentioned");
+ }
+
+ while i < 3 && j > 0 {
+ println!("KO - i and j not mentioned");
+ }
+
+ while i < 3 {
+ let mut i = 5;
+ fn_mutref(&mut i);
+ println!("KO - shadowed");
+ }
+
+ while i < 3 && j > 0 {
+ i = 5;
+ println!("OK - i in cond and mentioned");
+ }
+}
+
+fn used_immutable() {
+ let mut i = 0;
+
+ while i < 3 {
+ fn_constref(&i);
+ println!("KO - const reference");
+ }
+
+ while i < 3 {
+ fn_val(i);
+ println!("KO - passed by value");
+ }
+
+ while i < 3 {
+ println!("OK - passed by mutable reference");
+ fn_mutref(&mut i)
+ }
+
+ while i < 3 {
+ fn_mutref(&mut i);
+ println!("OK - passed by mutable reference");
+ }
+}
+
+const N: i32 = 5;
+const B: bool = false;
+
+fn consts() {
+ while false {
+ println!("Constants are not linted");
+ }
+
+ while B {
+ println!("Constants are not linted");
+ }
+
+ while N > 0 {
+ println!("Constants are not linted");
+ }
+}
+
+use std::cell::Cell;
+
+fn maybe_i_mutate(i: &Cell<bool>) {
+ unimplemented!()
+}
+
+fn internally_mutable() {
+ let b = Cell::new(true);
+
+ while b.get() {
+ // b cannot be silently coerced to `bool`
+ maybe_i_mutate(&b);
+ println!("OK - Method call within condition");
+ }
+}
+
+struct Counter {
+ count: usize,
+}
+
+impl Counter {
+ fn inc(&mut self) {
+ self.count += 1;
+ }
+
+ fn inc_n(&mut self, n: usize) {
+ while self.count < n {
+ self.inc();
+ }
+ println!("OK - self borrowed mutably");
+ }
+
+ fn print_n(&self, n: usize) {
+ while self.count < n {
+ println!("KO - {} is not mutated", self.count);
+ }
+ }
+}
+
+fn while_loop_with_break_and_return() {
+ let y = 0;
+ while y < 10 {
+ if y == 0 {
+ break;
+ }
+ println!("KO - loop contains break");
+ }
+
+ while y < 10 {
+ if y == 0 {
+ return;
+ }
+ println!("KO - loop contains return");
+ }
+}
+
+fn immutable_condition_false_positive(mut n: u64) -> u32 {
+ let mut count = 0;
+ while {
+ n >>= 1;
+ n != 0
+ } {
+ count += 1;
+ }
+ count
+}
+
+fn main() {
+ immutable_condition();
+ unused_var();
+ used_immutable();
+ internally_mutable();
+ immutable_condition_false_positive(5);
+
+ let mut c = Counter { count: 0 };
+ c.inc_n(5);
+ c.print_n(2);
+
+ while_loop_with_break_and_return();
+}
diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr
new file mode 100644
index 000000000..4ec7d900a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/infinite_loop.stderr
@@ -0,0 +1,95 @@
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:20:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: `#[deny(clippy::while_immutable_condition)]` on by default
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:25:11
+ |
+LL | while y < 10 && x < 3 {
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:32:11
+ |
+LL | while !cond {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:76:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:81:11
+ |
+LL | while i < 3 && j > 0 {
+ | ^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:85:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:100:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:105:11
+ |
+LL | while i < 3 {
+ | ^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:171:15
+ |
+LL | while self.count < n {
+ | ^^^^^^^^^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:179:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+ = note: this loop contains `return`s or `break`s
+ = help: rewrite it as `if cond { loop { } }`
+
+error: variables in the condition are not mutated in the loop body
+ --> $DIR/infinite_loop.rs:186:11
+ |
+LL | while y < 10 {
+ | ^^^^^^
+ |
+ = note: this may lead to an infinite or to a never running loop
+ = note: this loop contains `return`s or `break`s
+ = help: rewrite it as `if cond { loop { } }`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inherent_to_string.rs b/src/tools/clippy/tests/ui/inherent_to_string.rs
new file mode 100644
index 000000000..aeb0a0c1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inherent_to_string.rs
@@ -0,0 +1,106 @@
+#![warn(clippy::inherent_to_string)]
+#![deny(clippy::inherent_to_string_shadow_display)]
+
+use std::fmt;
+
+trait FalsePositive {
+ fn to_string(&self) -> String;
+}
+
+struct A;
+struct B;
+struct C;
+struct D;
+struct E;
+struct F;
+struct G;
+
+impl A {
+ // Should be detected; emit warning
+ fn to_string(&self) -> String {
+ "A.to_string()".to_string()
+ }
+
+ // Should not be detected as it does not match the function signature
+ fn to_str(&self) -> String {
+ "A.to_str()".to_string()
+ }
+}
+
+// Should not be detected as it is a free function
+fn to_string() -> String {
+ "free to_string()".to_string()
+}
+
+impl B {
+ // Should not be detected, wrong return type
+ fn to_string(&self) -> i32 {
+ 42
+ }
+}
+
+impl C {
+ // Should be detected and emit error as C also implements Display
+ fn to_string(&self) -> String {
+ "C.to_string()".to_string()
+ }
+}
+
+impl fmt::Display for C {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "impl Display for C")
+ }
+}
+
+impl FalsePositive for D {
+ // Should not be detected, as it is a trait function
+ fn to_string(&self) -> String {
+ "impl FalsePositive for D".to_string()
+ }
+}
+
+impl E {
+ // Should not be detected, as it is not bound to an instance
+ fn to_string() -> String {
+ "E::to_string()".to_string()
+ }
+}
+
+impl F {
+ // Should not be detected, as it does not match the function signature
+ fn to_string(&self, _i: i32) -> String {
+ "F.to_string()".to_string()
+ }
+}
+
+impl G {
+ // Should not be detected, as it does not match the function signature
+ fn to_string<const _N: usize>(&self) -> String {
+ "G.to_string()".to_string()
+ }
+}
+
+fn main() {
+ let a = A;
+ a.to_string();
+ a.to_str();
+
+ to_string();
+
+ let b = B;
+ b.to_string();
+
+ let c = C;
+ C.to_string();
+
+ let d = D;
+ d.to_string();
+
+ E::to_string();
+
+ let f = F;
+ f.to_string(1);
+
+ let g = G;
+ g.to_string::<1>();
+}
diff --git a/src/tools/clippy/tests/ui/inherent_to_string.stderr b/src/tools/clippy/tests/ui/inherent_to_string.stderr
new file mode 100644
index 000000000..4f331f5be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inherent_to_string.stderr
@@ -0,0 +1,28 @@
+error: implementation of inherent method `to_string(&self) -> String` for type `A`
+ --> $DIR/inherent_to_string.rs:20:5
+ |
+LL | / fn to_string(&self) -> String {
+LL | | "A.to_string()".to_string()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::inherent-to-string` implied by `-D warnings`
+ = help: implement trait `Display` for type `A` instead
+
+error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
+ --> $DIR/inherent_to_string.rs:44:5
+ |
+LL | / fn to_string(&self) -> String {
+LL | | "C.to_string()".to_string()
+LL | | }
+ | |_____^
+ |
+note: the lint level is defined here
+ --> $DIR/inherent_to_string.rs:2:9
+ |
+LL | #![deny(clippy::inherent_to_string_shadow_display)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: remove the inherent method from type `C`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.fixed b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed
new file mode 100644
index 000000000..fe21a71a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![warn(clippy::inline_fn_without_body)]
+#![allow(clippy::inline_always)]
+
+trait Foo {
+ fn default_inline();
+
+ fn always_inline();
+
+ fn never_inline();
+
+ #[inline]
+ fn has_body() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.rs b/src/tools/clippy/tests/ui/inline_fn_without_body.rs
new file mode 100644
index 000000000..507469894
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inline_fn_without_body.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+
+#![warn(clippy::inline_fn_without_body)]
+#![allow(clippy::inline_always)]
+
+trait Foo {
+ #[inline]
+ fn default_inline();
+
+ #[inline(always)]
+ fn always_inline();
+
+ #[inline(never)]
+ fn never_inline();
+
+ #[inline]
+ fn has_body() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.stderr b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr
new file mode 100644
index 000000000..32d35e209
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr
@@ -0,0 +1,28 @@
+error: use of `#[inline]` on trait method `default_inline` which has no body
+ --> $DIR/inline_fn_without_body.rs:7:5
+ |
+LL | #[inline]
+ | _____-^^^^^^^^
+LL | | fn default_inline();
+ | |____- help: remove
+ |
+ = note: `-D clippy::inline-fn-without-body` implied by `-D warnings`
+
+error: use of `#[inline]` on trait method `always_inline` which has no body
+ --> $DIR/inline_fn_without_body.rs:10:5
+ |
+LL | #[inline(always)]
+ | _____-^^^^^^^^^^^^^^^^
+LL | | fn always_inline();
+ | |____- help: remove
+
+error: use of `#[inline]` on trait method `never_inline` which has no body
+ --> $DIR/inline_fn_without_body.rs:13:5
+ |
+LL | #[inline(never)]
+ | _____-^^^^^^^^^^^^^^^
+LL | | fn never_inline();
+ | |____- help: remove
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/inspect_for_each.rs b/src/tools/clippy/tests/ui/inspect_for_each.rs
new file mode 100644
index 000000000..7fe45c83b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inspect_for_each.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::inspect_for_each)]
+
+fn main() {
+ let a: Vec<usize> = vec![1, 2, 3, 4, 5];
+
+ let mut b: Vec<usize> = Vec::new();
+ a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
+ let y = do_some(x);
+ let z = do_more(y);
+ b.push(z);
+ });
+
+ assert_eq!(b, vec![4, 5, 6, 7, 8]);
+}
+
+fn do_some(a: usize) -> usize {
+ a + 1
+}
+
+fn do_more(a: usize) -> usize {
+ a + 2
+}
diff --git a/src/tools/clippy/tests/ui/inspect_for_each.stderr b/src/tools/clippy/tests/ui/inspect_for_each.stderr
new file mode 100644
index 000000000..9f976bb74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/inspect_for_each.stderr
@@ -0,0 +1,16 @@
+error: called `inspect(..).for_each(..)` on an `Iterator`
+ --> $DIR/inspect_for_each.rs:7:19
+ |
+LL | a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
+ | ___________________^
+LL | | let y = do_some(x);
+LL | | let z = do_more(y);
+LL | | b.push(z);
+LL | | });
+ | |______^
+ |
+ = note: `-D clippy::inspect-for-each` implied by `-D warnings`
+ = help: move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/int_plus_one.fixed b/src/tools/clippy/tests/ui/int_plus_one.fixed
new file mode 100644
index 000000000..642830f24
--- /dev/null
+++ b/src/tools/clippy/tests/ui/int_plus_one.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+#[warn(clippy::int_plus_one)]
+fn main() {
+ let x = 1i32;
+ let y = 0i32;
+
+ let _ = x > y;
+ let _ = y < x;
+
+ let _ = x > y;
+ let _ = y < x;
+
+ let _ = x > y; // should be ok
+ let _ = y < x; // should be ok
+}
diff --git a/src/tools/clippy/tests/ui/int_plus_one.rs b/src/tools/clippy/tests/ui/int_plus_one.rs
new file mode 100644
index 000000000..0755a0c79
--- /dev/null
+++ b/src/tools/clippy/tests/ui/int_plus_one.rs
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+#[warn(clippy::int_plus_one)]
+fn main() {
+ let x = 1i32;
+ let y = 0i32;
+
+ let _ = x >= y + 1;
+ let _ = y + 1 <= x;
+
+ let _ = x - 1 >= y;
+ let _ = y <= x - 1;
+
+ let _ = x > y; // should be ok
+ let _ = y < x; // should be ok
+}
diff --git a/src/tools/clippy/tests/ui/int_plus_one.stderr b/src/tools/clippy/tests/ui/int_plus_one.stderr
new file mode 100644
index 000000000..c5b020ba8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/int_plus_one.stderr
@@ -0,0 +1,28 @@
+error: unnecessary `>= y + 1` or `x - 1 >=`
+ --> $DIR/int_plus_one.rs:9:13
+ |
+LL | let _ = x >= y + 1;
+ | ^^^^^^^^^^ help: change it to: `x > y`
+ |
+ = note: `-D clippy::int-plus-one` implied by `-D warnings`
+
+error: unnecessary `>= y + 1` or `x - 1 >=`
+ --> $DIR/int_plus_one.rs:10:13
+ |
+LL | let _ = y + 1 <= x;
+ | ^^^^^^^^^^ help: change it to: `y < x`
+
+error: unnecessary `>= y + 1` or `x - 1 >=`
+ --> $DIR/int_plus_one.rs:12:13
+ |
+LL | let _ = x - 1 >= y;
+ | ^^^^^^^^^^ help: change it to: `x > y`
+
+error: unnecessary `>= y + 1` or `x - 1 >=`
+ --> $DIR/int_plus_one.rs:13:13
+ |
+LL | let _ = y <= x - 1;
+ | ^^^^^^^^^^ help: change it to: `y < x`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs
new file mode 100644
index 000000000..67f24b454
--- /dev/null
+++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs
@@ -0,0 +1,102 @@
+#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)]
+
+#[rustfmt::skip]
+fn main() {
+ let mut i = 1i32;
+ let mut var1 = 0i32;
+ let mut var2 = -1i32;
+ 1 + i;
+ i * 2;
+ 1 %
+ i / 2; // no error, this is part of the expression in the preceding line
+ i - 2 + 2 - i;
+ -i;
+ i >> 1;
+ i << 1;
+
+ // no error, overflows are checked by `overflowing_literals`
+ -1;
+ -(-1);
+
+ i & 1; // no wrapping
+ i | 1;
+ i ^ 1;
+
+ i += 1;
+ i -= 1;
+ i *= 2;
+ i /= 2;
+ i /= 0;
+ i /= -1;
+ i /= var1;
+ i /= var2;
+ i %= 2;
+ i %= 0;
+ i %= -1;
+ i %= var1;
+ i %= var2;
+ i <<= 3;
+ i >>= 2;
+
+ // no errors
+ i |= 1;
+ i &= 1;
+ i ^= i;
+
+ // No errors for the following items because they are constant expressions
+ enum Foo {
+ Bar = -2,
+ }
+ struct Baz([i32; 1 + 1]);
+ union Qux {
+ field: [i32; 1 + 1],
+ }
+ type Alias = [i32; 1 + 1];
+
+ const FOO: i32 = -2;
+ static BAR: i32 = -2;
+
+ let _: [i32; 1 + 1] = [0, 0];
+
+ let _: [i32; 1 + 1] = {
+ let a: [i32; 1 + 1] = [0, 0];
+ a
+ };
+
+ trait Trait {
+ const ASSOC: i32 = 1 + 1;
+ }
+
+ impl Trait for Foo {
+ const ASSOC: i32 = {
+ let _: [i32; 1 + 1];
+ fn foo() {}
+ 1 + 1
+ };
+ }
+}
+
+// warn on references as well! (#5328)
+pub fn int_arith_ref() {
+ 3 + &1;
+ &3 + 1;
+ &3 + &1;
+}
+
+pub fn foo(x: &i32) -> i32 {
+ let a = 5;
+ a + x
+}
+
+pub fn bar(x: &i32, y: &i32) -> i32 {
+ x + y
+}
+
+pub fn baz(x: i32, y: &i32) -> i32 {
+ x + y
+}
+
+pub fn qux(x: i32, y: i32) -> i32 {
+ (&x + &y)
+}
diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.stderr b/src/tools/clippy/tests/ui/integer_arithmetic.stderr
new file mode 100644
index 000000000..9a795b1f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/integer_arithmetic.stderr
@@ -0,0 +1,169 @@
+error: this operation will panic at runtime
+ --> $DIR/integer_arithmetic.rs:30:5
+ |
+LL | i /= 0;
+ | ^^^^^^ attempt to divide `_` by zero
+ |
+ = note: `#[deny(unconditional_panic)]` on by default
+
+error: this operation will panic at runtime
+ --> $DIR/integer_arithmetic.rs:35:5
+ |
+LL | i %= 0;
+ | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:9:5
+ |
+LL | 1 + i;
+ | ^^^^^
+ |
+ = note: `-D clippy::integer-arithmetic` implied by `-D warnings`
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:10:5
+ |
+LL | i * 2;
+ | ^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:11:5
+ |
+LL | / 1 %
+LL | | i / 2; // no error, this is part of the expression in the preceding line
+ | |_____^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:13:5
+ |
+LL | i - 2 + 2 - i;
+ | ^^^^^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:14:5
+ |
+LL | -i;
+ | ^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:15:5
+ |
+LL | i >> 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:16:5
+ |
+LL | i << 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:26:5
+ |
+LL | i += 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:27:5
+ |
+LL | i -= 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:28:5
+ |
+LL | i *= 2;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:31:11
+ |
+LL | i /= -1;
+ | ^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:32:5
+ |
+LL | i /= var1;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:33:5
+ |
+LL | i /= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:36:11
+ |
+LL | i %= -1;
+ | ^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:37:5
+ |
+LL | i %= var1;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:38:5
+ |
+LL | i %= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:39:5
+ |
+LL | i <<= 3;
+ | ^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:40:5
+ |
+LL | i >>= 2;
+ | ^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:82:5
+ |
+LL | 3 + &1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:83:5
+ |
+LL | &3 + 1;
+ | ^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:84:5
+ |
+LL | &3 + &1;
+ | ^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:89:5
+ |
+LL | a + x
+ | ^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:93:5
+ |
+LL | x + y
+ | ^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:97:5
+ |
+LL | x + y
+ | ^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:101:5
+ |
+LL | (&x + &y)
+ | ^^^^^^^^^
+
+error: aborting due to 27 previous errors
+
diff --git a/src/tools/clippy/tests/ui/integer_division.rs b/src/tools/clippy/tests/ui/integer_division.rs
new file mode 100644
index 000000000..800c75257
--- /dev/null
+++ b/src/tools/clippy/tests/ui/integer_division.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::integer_division)]
+
+fn main() {
+ let two = 2;
+ let n = 1 / 2;
+ let o = 1 / two;
+ let p = two / 4;
+ let x = 1. / 2.0;
+}
diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr
new file mode 100644
index 000000000..cbb7f8814
--- /dev/null
+++ b/src/tools/clippy/tests/ui/integer_division.stderr
@@ -0,0 +1,27 @@
+error: integer division
+ --> $DIR/integer_division.rs:5:13
+ |
+LL | let n = 1 / 2;
+ | ^^^^^
+ |
+ = note: `-D clippy::integer-division` implied by `-D warnings`
+ = help: division of integers may cause loss of precision. consider using floats
+
+error: integer division
+ --> $DIR/integer_division.rs:6:13
+ |
+LL | let o = 1 / two;
+ | ^^^^^^^
+ |
+ = help: division of integers may cause loss of precision. consider using floats
+
+error: integer division
+ --> $DIR/integer_division.rs:7:13
+ |
+LL | let p = two / 4;
+ | ^^^^^^^
+ |
+ = help: division of integers may cause loss of precision. consider using floats
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
new file mode 100644
index 000000000..b77f17944
--- /dev/null
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+#![allow(clippy::useless_vec, clippy::needless_borrow)]
+#![warn(clippy::into_iter_on_ref)]
+
+struct X;
+use std::collections::*;
+
+fn main() {
+ for _ in &[1, 2, 3] {}
+ for _ in vec![X, X] {}
+ for _ in &vec![X, X] {}
+
+ let _ = vec![1, 2, 3].into_iter();
+ let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter()
+ let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter()
+ let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
+ let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
+
+ let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
+ let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
+ let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut()
+
+ let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Vec::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&VecDeque::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&LinkedList::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+ let _ = (&HashMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
+
+ let _ = (&BTreeSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&BinaryHeap::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
+ let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter()
+ let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter()
+
+ let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter()
+}
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
new file mode 100644
index 000000000..3854bb05a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+#![allow(clippy::useless_vec, clippy::needless_borrow)]
+#![warn(clippy::into_iter_on_ref)]
+
+struct X;
+use std::collections::*;
+
+fn main() {
+ for _ in &[1, 2, 3] {}
+ for _ in vec![X, X] {}
+ for _ in &vec![X, X] {}
+
+ let _ = vec![1, 2, 3].into_iter();
+ let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
+ let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
+ let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+ let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+
+ let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+ let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+ let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
+
+ let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+
+ let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
+ let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
+
+ let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
+}
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
new file mode 100644
index 000000000..28003b365
--- /dev/null
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
@@ -0,0 +1,166 @@
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
+ --> $DIR/into_iter_on_ref.rs:14:30
+ |
+LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+ |
+ = note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
+ --> $DIR/into_iter_on_ref.rs:15:46
+ |
+LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
+ --> $DIR/into_iter_on_ref.rs:16:41
+ |
+LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
+ --> $DIR/into_iter_on_ref.rs:17:44
+ |
+LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
+ --> $DIR/into_iter_on_ref.rs:19:32
+ |
+LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
+ --> $DIR/into_iter_on_ref.rs:20:36
+ |
+LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array`
+ --> $DIR/into_iter_on_ref.rs:21:40
+ |
+LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option`
+ --> $DIR/into_iter_on_ref.rs:23:24
+ |
+LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option`
+ --> $DIR/into_iter_on_ref.rs:24:28
+ |
+LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result`
+ --> $DIR/into_iter_on_ref.rs:25:32
+ |
+LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result`
+ --> $DIR/into_iter_on_ref.rs:26:37
+ |
+LL | let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
+ --> $DIR/into_iter_on_ref.rs:27:34
+ |
+LL | let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec`
+ --> $DIR/into_iter_on_ref.rs:28:38
+ |
+LL | let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap`
+ --> $DIR/into_iter_on_ref.rs:29:44
+ |
+LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap`
+ --> $DIR/into_iter_on_ref.rs:30:48
+ |
+LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque`
+ --> $DIR/into_iter_on_ref.rs:31:39
+ |
+LL | let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque`
+ --> $DIR/into_iter_on_ref.rs:32:43
+ |
+LL | let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList`
+ --> $DIR/into_iter_on_ref.rs:33:41
+ |
+LL | let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList`
+ --> $DIR/into_iter_on_ref.rs:34:45
+ |
+LL | let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap`
+ --> $DIR/into_iter_on_ref.rs:35:43
+ |
+LL | let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap`
+ --> $DIR/into_iter_on_ref.rs:36:47
+ |
+LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
+ | ^^^^^^^^^ help: call directly: `iter_mut`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet`
+ --> $DIR/into_iter_on_ref.rs:38:39
+ |
+LL | let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap`
+ --> $DIR/into_iter_on_ref.rs:39:41
+ |
+LL | let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet`
+ --> $DIR/into_iter_on_ref.rs:40:38
+ |
+LL | let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path`
+ --> $DIR/into_iter_on_ref.rs:41:43
+ |
+LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf`
+ --> $DIR/into_iter_on_ref.rs:42:47
+ |
+LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
+ --> $DIR/into_iter_on_ref.rs:44:26
+ |
+LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
+ | ^^^^^^^^^ help: call directly: `iter`
+
+error: aborting due to 27 previous errors
+
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
new file mode 100644
index 000000000..4f5322ebf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+ unsafe {
+ let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+ let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ struct A; // zero sized struct
+ assert_eq!(std::mem::size_of::<A>(), 0);
+
+ let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+ let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+
+ let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
+ std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+
+ std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+ std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+
+ std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+
+ std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs
new file mode 100644
index 000000000..ae51c52d8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+ unsafe {
+ let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+ let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+
+ let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+ std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+ struct A; // zero sized struct
+ assert_eq!(std::mem::size_of::<A>(), 0);
+
+ let _a: A = std::ptr::read(std::ptr::null());
+ let _a: A = std::ptr::read(std::ptr::null_mut());
+
+ let _a: A = std::ptr::read_unaligned(std::ptr::null());
+ let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+
+ let _a: A = std::ptr::read_volatile(std::ptr::null());
+ let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+
+ let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+
+ let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+ std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+ std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+
+ std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+ std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+
+ std::ptr::write(std::ptr::null_mut(), A);
+
+ std::ptr::write_unaligned(std::ptr::null_mut(), A);
+
+ std::ptr::write_volatile(std::ptr::null_mut(), A);
+
+ std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
new file mode 100644
index 000000000..532c36abe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
@@ -0,0 +1,154 @@
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:5:59
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+ |
+ = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:6:59
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:8:63
+ |
+LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:10:33
+ |
+LL | std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:11:73
+ |
+LL | std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:13:48
+ |
+LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:14:88
+ |
+LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:19:36
+ |
+LL | let _a: A = std::ptr::read(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:20:36
+ |
+LL | let _a: A = std::ptr::read(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:22:46
+ |
+LL | let _a: A = std::ptr::read_unaligned(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:23:46
+ |
+LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:25:45
+ |
+LL | let _a: A = std::ptr::read_volatile(std::ptr::null());
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:26:45
+ |
+LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:28:39
+ |
+LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:30:69
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+ | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:31:69
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:33:73
+ |
+LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:35:29
+ |
+LL | std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:36:37
+ |
+LL | std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:38:44
+ |
+LL | std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:39:52
+ |
+LL | std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:41:25
+ |
+LL | std::ptr::write(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:43:35
+ |
+LL | std::ptr::write_unaligned(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:45:34
+ |
+LL | std::ptr::write_volatile(std::ptr::null_mut(), A);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+ --> $DIR/invalid_null_ptr_usage.rs:47:40
+ |
+LL | std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+ | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
new file mode 100644
index 000000000..697416dce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
@@ -0,0 +1,85 @@
+#![warn(clippy::invalid_upcast_comparisons)]
+#![allow(
+ unused,
+ clippy::eq_op,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::cast_lossless
+)]
+
+fn mk_value<T>() -> T {
+ unimplemented!()
+}
+
+fn main() {
+ let u32: u32 = mk_value();
+ let u8: u8 = mk_value();
+ let i32: i32 = mk_value();
+ let i8: i8 = mk_value();
+
+ // always false, since no u8 can be > 300
+ (u8 as u32) > 300;
+ (u8 as i32) > 300;
+ (u8 as u32) == 300;
+ (u8 as i32) == 300;
+ 300 < (u8 as u32);
+ 300 < (u8 as i32);
+ 300 == (u8 as u32);
+ 300 == (u8 as i32);
+ // inverted of the above
+ (u8 as u32) <= 300;
+ (u8 as i32) <= 300;
+ (u8 as u32) != 300;
+ (u8 as i32) != 300;
+ 300 >= (u8 as u32);
+ 300 >= (u8 as i32);
+ 300 != (u8 as u32);
+ 300 != (u8 as i32);
+
+ // always false, since u8 -> i32 doesn't wrap
+ (u8 as i32) < 0;
+ -5 != (u8 as i32);
+ // inverted of the above
+ (u8 as i32) >= 0;
+ -5 == (u8 as i32);
+
+ // always false, since no u8 can be 1337
+ 1337 == (u8 as i32);
+ 1337 == (u8 as u32);
+ // inverted of the above
+ 1337 != (u8 as i32);
+ 1337 != (u8 as u32);
+
+ // Those are Ok:
+ (u8 as u32) > 20;
+ 42 == (u8 as i32);
+ 42 != (u8 as i32);
+ 42 > (u8 as i32);
+ (u8 as i32) == 42;
+ (u8 as i32) != 42;
+ (u8 as i32) > 42;
+ (u8 as i32) < 42;
+
+ (u8 as i8) == -1;
+ (u8 as i8) != -1;
+ (u8 as i32) > -1;
+ (u8 as i32) < -1;
+ (u32 as i32) < -5;
+ (u32 as i32) < 10;
+
+ (i8 as u8) == 1;
+ (i8 as u8) != 1;
+ (i8 as u8) < 1;
+ (i8 as u8) > 1;
+ (i32 as u32) < 5;
+ (i32 as u32) < 10;
+
+ -5 < (u32 as i32);
+ 0 <= (u32 as i32);
+ 0 < (u32 as i32);
+
+ -5 > (u32 as i32);
+ -5 >= (u8 as i32);
+
+ -5 == (u32 as i32);
+}
diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
new file mode 100644
index 000000000..03c3fb80a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
@@ -0,0 +1,166 @@
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:21:5
+ |
+LL | (u8 as u32) > 300;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::invalid-upcast-comparisons` implied by `-D warnings`
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:22:5
+ |
+LL | (u8 as i32) > 300;
+ | ^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:23:5
+ |
+LL | (u8 as u32) == 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:24:5
+ |
+LL | (u8 as i32) == 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:25:5
+ |
+LL | 300 < (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:26:5
+ |
+LL | 300 < (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:27:5
+ |
+LL | 300 == (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:28:5
+ |
+LL | 300 == (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:30:5
+ |
+LL | (u8 as u32) <= 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:31:5
+ |
+LL | (u8 as i32) <= 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:32:5
+ |
+LL | (u8 as u32) != 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:33:5
+ |
+LL | (u8 as i32) != 300;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:34:5
+ |
+LL | 300 >= (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:35:5
+ |
+LL | 300 >= (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:36:5
+ |
+LL | 300 != (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:37:5
+ |
+LL | 300 != (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:40:5
+ |
+LL | (u8 as i32) < 0;
+ | ^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:41:5
+ |
+LL | -5 != (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:43:5
+ |
+LL | (u8 as i32) >= 0;
+ | ^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:44:5
+ |
+LL | -5 == (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:47:5
+ |
+LL | 1337 == (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:48:5
+ |
+LL | 1337 == (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:50:5
+ |
+LL | 1337 != (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:51:5
+ |
+LL | 1337 != (u8 as u32);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always true
+ --> $DIR/invalid_upcast_comparisons.rs:65:5
+ |
+LL | (u8 as i32) > -1;
+ | ^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:66:5
+ |
+LL | (u8 as i32) < -1;
+ | ^^^^^^^^^^^^^^^^
+
+error: because of the numeric bounds on `u8` prior to casting, this expression is always false
+ --> $DIR/invalid_upcast_comparisons.rs:82:5
+ |
+LL | -5 >= (u8 as i32);
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 27 previous errors
+
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
new file mode 100644
index 000000000..3dc096d31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::invalid_utf8_in_unchecked)]
+
+fn main() {
+ // Valid
+ unsafe {
+ std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]);
+ std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']);
+ std::str::from_utf8_unchecked(b"clippy");
+
+ let x = 0xA0;
+ std::str::from_utf8_unchecked(&[0xC0, x]);
+ }
+
+ // Invalid
+ unsafe {
+ std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+ std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']);
+ std::str::from_utf8_unchecked(b"cl\x82ippy");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
new file mode 100644
index 000000000..c89cd2758
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
@@ -0,0 +1,22 @@
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:16:9
+ |
+LL | std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings`
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:17:9
+ |
+LL | std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:18:9
+ |
+LL | std::str::from_utf8_unchecked(b"cl/x82ippy");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed b/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed
new file mode 100644
index 000000000..c0ba647d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_ascii_digit();
+ let _ = c.is_ascii_hexdigit();
+ let _ = c.is_ascii_hexdigit();
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs b/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs
new file mode 100644
index 000000000..68e3f3243
--- /dev/null
+++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_digit(10);
+ let _ = c.is_digit(16);
+ let _ = c.is_digit(0x10);
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr b/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr
new file mode 100644
index 000000000..dc5cb2913
--- /dev/null
+++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr
@@ -0,0 +1,22 @@
+error: use of `char::is_digit` with literal radix of 10
+ --> $DIR/is_digit_ascii_radix.rs:11:13
+ |
+LL | let _ = c.is_digit(10);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()`
+ |
+ = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:12:13
+ |
+LL | let _ = c.is_digit(16);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:13:13
+ |
+LL | let _ = c.is_digit(0x10);
+ | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs
new file mode 100644
index 000000000..586d13647
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue-3145.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("{}" a); //~ERROR expected `,`, found `a`
+}
diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr
new file mode 100644
index 000000000..a35032aa1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue-3145.stderr
@@ -0,0 +1,8 @@
+error: expected `,`, found `a`
+ --> $DIR/issue-3145.rs:2:19
+ |
+LL | println!("{}" a); //~ERROR expected `,`, found `a`
+ | ^ expected `,`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/issue-7447.rs b/src/tools/clippy/tests/ui/issue-7447.rs
new file mode 100644
index 000000000..fdb77f322
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue-7447.rs
@@ -0,0 +1,25 @@
+use std::{borrow::Cow, collections::BTreeMap, marker::PhantomData, sync::Arc};
+
+fn byte_view<'a>(s: &'a ByteView<'_>) -> BTreeMap<&'a str, ByteView<'a>> {
+ panic!()
+}
+
+fn group_entries(s: &()) -> BTreeMap<Cow<'_, str>, Vec<Cow<'_, str>>> {
+ todo!()
+}
+
+struct Mmap;
+
+enum ByteViewBacking<'a> {
+ Buf(Cow<'a, [u8]>),
+ Mmap(Mmap),
+}
+
+pub struct ByteView<'a> {
+ backing: Arc<ByteViewBacking<'a>>,
+}
+
+fn main() {
+ byte_view(panic!());
+ group_entries(panic!());
+}
diff --git a/src/tools/clippy/tests/ui/issue-7447.stderr b/src/tools/clippy/tests/ui/issue-7447.stderr
new file mode 100644
index 000000000..8d8c29f13
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue-7447.stderr
@@ -0,0 +1,19 @@
+error: sub-expression diverges
+ --> $DIR/issue-7447.rs:23:15
+ |
+LL | byte_view(panic!());
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: sub-expression diverges
+ --> $DIR/issue-7447.rs:24:19
+ |
+LL | group_entries(panic!());
+ | ^^^^^^^^
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/issue_2356.fixed b/src/tools/clippy/tests/ui/issue_2356.fixed
new file mode 100644
index 000000000..942e99fa8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue_2356.fixed
@@ -0,0 +1,26 @@
+// run-rustfix
+#![deny(clippy::while_let_on_iterator)]
+#![allow(unused_mut)]
+
+use std::iter::Iterator;
+
+struct Foo;
+
+impl Foo {
+ fn foo1<I: Iterator<Item = usize>>(mut it: I) {
+ while let Some(_) = it.next() {
+ println!("{:?}", it.size_hint());
+ }
+ }
+
+ fn foo2<I: Iterator<Item = usize>>(mut it: I) {
+ for e in it {
+ println!("{:?}", e);
+ }
+ }
+}
+
+fn main() {
+ Foo::foo1(vec![].into_iter());
+ Foo::foo2(vec![].into_iter());
+}
diff --git a/src/tools/clippy/tests/ui/issue_2356.rs b/src/tools/clippy/tests/ui/issue_2356.rs
new file mode 100644
index 000000000..b000234ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue_2356.rs
@@ -0,0 +1,26 @@
+// run-rustfix
+#![deny(clippy::while_let_on_iterator)]
+#![allow(unused_mut)]
+
+use std::iter::Iterator;
+
+struct Foo;
+
+impl Foo {
+ fn foo1<I: Iterator<Item = usize>>(mut it: I) {
+ while let Some(_) = it.next() {
+ println!("{:?}", it.size_hint());
+ }
+ }
+
+ fn foo2<I: Iterator<Item = usize>>(mut it: I) {
+ while let Some(e) = it.next() {
+ println!("{:?}", e);
+ }
+ }
+}
+
+fn main() {
+ Foo::foo1(vec![].into_iter());
+ Foo::foo2(vec![].into_iter());
+}
diff --git a/src/tools/clippy/tests/ui/issue_2356.stderr b/src/tools/clippy/tests/ui/issue_2356.stderr
new file mode 100644
index 000000000..4e3ff7522
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue_2356.stderr
@@ -0,0 +1,14 @@
+error: this loop could be written as a `for` loop
+ --> $DIR/issue_2356.rs:17:9
+ |
+LL | while let Some(e) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it`
+ |
+note: the lint level is defined here
+ --> $DIR/issue_2356.rs:2:9
+ |
+LL | #![deny(clippy::while_let_on_iterator)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/issue_4266.rs b/src/tools/clippy/tests/ui/issue_4266.rs
new file mode 100644
index 000000000..d9d48189b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue_4266.rs
@@ -0,0 +1,38 @@
+#![allow(dead_code)]
+
+async fn sink1<'a>(_: &'a str) {} // lint
+async fn sink1_elided(_: &str) {} // ok
+
+// lint
+async fn one_to_one<'a>(s: &'a str) -> &'a str {
+ s
+}
+
+// ok
+async fn one_to_one_elided(s: &str) -> &str {
+ s
+}
+
+// ok
+async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str {
+ a
+}
+
+// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn
+
+// #3988
+struct Foo;
+impl Foo {
+ // ok
+ pub async fn new(&mut self) -> Self {
+ Foo {}
+ }
+}
+
+// rust-lang/rust#61115
+// ok
+async fn print(s: &str) {
+ println!("{}", s);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr
new file mode 100644
index 000000000..e5042aaa7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/issue_4266.stderr
@@ -0,0 +1,25 @@
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/issue_4266.rs:3:1
+ |
+LL | async fn sink1<'a>(_: &'a str) {} // lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/issue_4266.rs:7:1
+ |
+LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually take no `self`
+ --> $DIR/issue_4266.rs:27:22
+ |
+LL | pub async fn new(&mut self) -> Self {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/item_after_statement.rs
new file mode 100644
index 000000000..d439ca1e4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/item_after_statement.rs
@@ -0,0 +1,52 @@
+#![warn(clippy::items_after_statements)]
+
+fn ok() {
+ fn foo() {
+ println!("foo");
+ }
+ foo();
+}
+
+fn last() {
+ foo();
+ fn foo() {
+ println!("foo");
+ }
+}
+
+fn main() {
+ foo();
+ fn foo() {
+ println!("foo");
+ }
+ foo();
+}
+
+fn mac() {
+ let mut a = 5;
+ println!("{}", a);
+ // do not lint this, because it needs to be after `a`
+ macro_rules! b {
+ () => {{
+ a = 6;
+ fn say_something() {
+ println!("something");
+ }
+ }};
+ }
+ b!();
+ println!("{}", a);
+}
+
+fn semicolon() {
+ struct S {
+ a: u32,
+ };
+ impl S {
+ fn new(a: u32) -> Self {
+ Self { a }
+ }
+ }
+
+ let _ = S::new(3);
+}
diff --git a/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/item_after_statement.stderr
new file mode 100644
index 000000000..ab4a6374c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/item_after_statement.stderr
@@ -0,0 +1,33 @@
+error: adding items after statements is confusing, since items exist from the start of the scope
+ --> $DIR/item_after_statement.rs:12:5
+ |
+LL | / fn foo() {
+LL | | println!("foo");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::items-after-statements` implied by `-D warnings`
+
+error: adding items after statements is confusing, since items exist from the start of the scope
+ --> $DIR/item_after_statement.rs:19:5
+ |
+LL | / fn foo() {
+LL | | println!("foo");
+LL | | }
+ | |_____^
+
+error: adding items after statements is confusing, since items exist from the start of the scope
+ --> $DIR/item_after_statement.rs:32:13
+ |
+LL | / fn say_something() {
+LL | | println!("something");
+LL | | }
+ | |_____________^
+...
+LL | b!();
+ | ---- in this macro invocation
+ |
+ = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
new file mode 100644
index 000000000..9b8621335
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![allow(unused)]
+
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+ let v = [1, 2, 3, 4, 5];
+ let v2: Vec<isize> = v.to_vec();
+ let v3: HashSet<isize> = v.iter().cloned().collect();
+ let v4: VecDeque<isize> = v.iter().cloned().collect();
+
+ // Handle macro expansion in suggestion
+ let _: Vec<isize> = vec![1, 2, 3].to_vec();
+
+ // Issue #3704
+ unsafe {
+ let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
+ .to_bytes().to_vec();
+ }
+
+ // Issue #6808
+ let arr: [u8; 64] = [0; 64];
+ let _: Vec<_> = arr.to_vec();
+
+ // Issue #6703
+ let _: Vec<isize> = v.to_vec();
+}
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
new file mode 100644
index 000000000..639f50665
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+
+#![allow(unused)]
+
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+ let v = [1, 2, 3, 4, 5];
+ let v2: Vec<isize> = v.iter().cloned().collect();
+ let v3: HashSet<isize> = v.iter().cloned().collect();
+ let v4: VecDeque<isize> = v.iter().cloned().collect();
+
+ // Handle macro expansion in suggestion
+ let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
+
+ // Issue #3704
+ unsafe {
+ let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
+ .to_bytes()
+ .iter()
+ .cloned()
+ .collect();
+ }
+
+ // Issue #6808
+ let arr: [u8; 64] = [0; 64];
+ let _: Vec<_> = arr.iter().cloned().collect();
+
+ // Issue #6703
+ let _: Vec<isize> = v.iter().copied().collect();
+}
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
new file mode 100644
index 000000000..b2cc497bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
@@ -0,0 +1,38 @@
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:10:27
+ |
+LL | let v2: Vec<isize> = v.iter().cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+ |
+ = note: `-D clippy::iter-cloned-collect` implied by `-D warnings`
+
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:15:38
+ |
+LL | let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:20:24
+ |
+LL | .to_bytes()
+ | ________________________^
+LL | | .iter()
+LL | | .cloned()
+LL | | .collect();
+ | |______________________^ help: try: `.to_vec()`
+
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:28:24
+ |
+LL | let _: Vec<_> = arr.iter().cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:31:26
+ |
+LL | let _: Vec<isize> = v.iter().copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_count.fixed b/src/tools/clippy/tests/ui/iter_count.fixed
new file mode 100644
index 000000000..90a6eef75
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_count.fixed
@@ -0,0 +1,87 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_count)]
+#![allow(
+ unused_variables,
+ array_into_iter,
+ unused_mut,
+ clippy::into_iter_on_ref,
+ clippy::unnecessary_operation
+)]
+
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+
+/// Struct to generate false positives for things with `.iter()`.
+#[derive(Copy, Clone)]
+struct HasIter;
+
+impl HasIter {
+ fn iter(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+
+ fn iter_mut(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+
+ fn into_iter(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+}
+
+#[allow(unused_must_use)]
+fn main() {
+ let mut vec = vec![0, 1, 2, 3];
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect();
+ let mut hash_set = HashSet::new();
+ let mut hash_map = HashMap::new();
+ let mut b_tree_map = BTreeMap::new();
+ let mut b_tree_set = BTreeSet::new();
+ let mut linked_list = LinkedList::new();
+ let mut binary_heap = BinaryHeap::new();
+ hash_set.insert(1);
+ hash_map.insert(1, 2);
+ b_tree_map.insert(1, 2);
+ b_tree_set.insert(1);
+ linked_list.push_back(1);
+ binary_heap.push(1);
+
+ &vec[..].len();
+ vec.len();
+ boxed_slice.len();
+ vec_deque.len();
+ hash_set.len();
+ hash_map.len();
+ b_tree_map.len();
+ b_tree_set.len();
+ linked_list.len();
+ binary_heap.len();
+
+ vec.len();
+ &vec[..].len();
+ vec_deque.len();
+ hash_map.len();
+ b_tree_map.len();
+ linked_list.len();
+
+ &vec[..].len();
+ vec.len();
+ vec_deque.len();
+ hash_set.len();
+ hash_map.len();
+ b_tree_map.len();
+ b_tree_set.len();
+ linked_list.len();
+ binary_heap.len();
+
+ // Make sure we don't lint for non-relevant types.
+ let false_positive = HasIter;
+ false_positive.iter().count();
+ false_positive.iter_mut().count();
+ false_positive.into_iter().count();
+}
diff --git a/src/tools/clippy/tests/ui/iter_count.rs b/src/tools/clippy/tests/ui/iter_count.rs
new file mode 100644
index 000000000..6681a480a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_count.rs
@@ -0,0 +1,87 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_count)]
+#![allow(
+ unused_variables,
+ array_into_iter,
+ unused_mut,
+ clippy::into_iter_on_ref,
+ clippy::unnecessary_operation
+)]
+
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+
+/// Struct to generate false positives for things with `.iter()`.
+#[derive(Copy, Clone)]
+struct HasIter;
+
+impl HasIter {
+ fn iter(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+
+ fn iter_mut(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+
+ fn into_iter(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+}
+
+#[allow(unused_must_use)]
+fn main() {
+ let mut vec = vec![0, 1, 2, 3];
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect();
+ let mut hash_set = HashSet::new();
+ let mut hash_map = HashMap::new();
+ let mut b_tree_map = BTreeMap::new();
+ let mut b_tree_set = BTreeSet::new();
+ let mut linked_list = LinkedList::new();
+ let mut binary_heap = BinaryHeap::new();
+ hash_set.insert(1);
+ hash_map.insert(1, 2);
+ b_tree_map.insert(1, 2);
+ b_tree_set.insert(1);
+ linked_list.push_back(1);
+ binary_heap.push(1);
+
+ &vec[..].iter().count();
+ vec.iter().count();
+ boxed_slice.iter().count();
+ vec_deque.iter().count();
+ hash_set.iter().count();
+ hash_map.iter().count();
+ b_tree_map.iter().count();
+ b_tree_set.iter().count();
+ linked_list.iter().count();
+ binary_heap.iter().count();
+
+ vec.iter_mut().count();
+ &vec[..].iter_mut().count();
+ vec_deque.iter_mut().count();
+ hash_map.iter_mut().count();
+ b_tree_map.iter_mut().count();
+ linked_list.iter_mut().count();
+
+ &vec[..].into_iter().count();
+ vec.into_iter().count();
+ vec_deque.into_iter().count();
+ hash_set.into_iter().count();
+ hash_map.into_iter().count();
+ b_tree_map.into_iter().count();
+ b_tree_set.into_iter().count();
+ linked_list.into_iter().count();
+ binary_heap.into_iter().count();
+
+ // Make sure we don't lint for non-relevant types.
+ let false_positive = HasIter;
+ false_positive.iter().count();
+ false_positive.iter_mut().count();
+ false_positive.into_iter().count();
+}
diff --git a/src/tools/clippy/tests/ui/iter_count.stderr b/src/tools/clippy/tests/ui/iter_count.stderr
new file mode 100644
index 000000000..2e3d7fc35
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_count.stderr
@@ -0,0 +1,154 @@
+error: called `.iter().count()` on a `slice`
+ --> $DIR/iter_count.rs:54:6
+ |
+LL | &vec[..].iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
+ |
+ = note: `-D clippy::iter-count` implied by `-D warnings`
+
+error: called `.iter().count()` on a `Vec`
+ --> $DIR/iter_count.rs:55:5
+ |
+LL | vec.iter().count();
+ | ^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
+
+error: called `.iter().count()` on a `slice`
+ --> $DIR/iter_count.rs:56:5
+ |
+LL | boxed_slice.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()`
+
+error: called `.iter().count()` on a `VecDeque`
+ --> $DIR/iter_count.rs:57:5
+ |
+LL | vec_deque.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
+
+error: called `.iter().count()` on a `HashSet`
+ --> $DIR/iter_count.rs:58:5
+ |
+LL | hash_set.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()`
+
+error: called `.iter().count()` on a `HashMap`
+ --> $DIR/iter_count.rs:59:5
+ |
+LL | hash_map.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
+
+error: called `.iter().count()` on a `BTreeMap`
+ --> $DIR/iter_count.rs:60:5
+ |
+LL | b_tree_map.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
+
+error: called `.iter().count()` on a `BTreeSet`
+ --> $DIR/iter_count.rs:61:5
+ |
+LL | b_tree_set.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()`
+
+error: called `.iter().count()` on a `LinkedList`
+ --> $DIR/iter_count.rs:62:5
+ |
+LL | linked_list.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
+
+error: called `.iter().count()` on a `BinaryHeap`
+ --> $DIR/iter_count.rs:63:5
+ |
+LL | binary_heap.iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()`
+
+error: called `.iter_mut().count()` on a `Vec`
+ --> $DIR/iter_count.rs:65:5
+ |
+LL | vec.iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
+
+error: called `.iter_mut().count()` on a `slice`
+ --> $DIR/iter_count.rs:66:6
+ |
+LL | &vec[..].iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
+
+error: called `.iter_mut().count()` on a `VecDeque`
+ --> $DIR/iter_count.rs:67:5
+ |
+LL | vec_deque.iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
+
+error: called `.iter_mut().count()` on a `HashMap`
+ --> $DIR/iter_count.rs:68:5
+ |
+LL | hash_map.iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
+
+error: called `.iter_mut().count()` on a `BTreeMap`
+ --> $DIR/iter_count.rs:69:5
+ |
+LL | b_tree_map.iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
+
+error: called `.iter_mut().count()` on a `LinkedList`
+ --> $DIR/iter_count.rs:70:5
+ |
+LL | linked_list.iter_mut().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
+
+error: called `.into_iter().count()` on a `slice`
+ --> $DIR/iter_count.rs:72:6
+ |
+LL | &vec[..].into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()`
+
+error: called `.into_iter().count()` on a `Vec`
+ --> $DIR/iter_count.rs:73:5
+ |
+LL | vec.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()`
+
+error: called `.into_iter().count()` on a `VecDeque`
+ --> $DIR/iter_count.rs:74:5
+ |
+LL | vec_deque.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()`
+
+error: called `.into_iter().count()` on a `HashSet`
+ --> $DIR/iter_count.rs:75:5
+ |
+LL | hash_set.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()`
+
+error: called `.into_iter().count()` on a `HashMap`
+ --> $DIR/iter_count.rs:76:5
+ |
+LL | hash_map.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()`
+
+error: called `.into_iter().count()` on a `BTreeMap`
+ --> $DIR/iter_count.rs:77:5
+ |
+LL | b_tree_map.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()`
+
+error: called `.into_iter().count()` on a `BTreeSet`
+ --> $DIR/iter_count.rs:78:5
+ |
+LL | b_tree_set.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()`
+
+error: called `.into_iter().count()` on a `LinkedList`
+ --> $DIR/iter_count.rs:79:5
+ |
+LL | linked_list.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()`
+
+error: called `.into_iter().count()` on a `BinaryHeap`
+ --> $DIR/iter_count.rs:80:5
+ |
+LL | binary_heap.into_iter().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()`
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.fixed b/src/tools/clippy/tests/ui/iter_next_slice.fixed
new file mode 100644
index 000000000..f612d26aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_next_slice.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::iter_next_slice)]
+
+fn main() {
+ // test code goes here
+ let s = [1, 2, 3];
+ let v = vec![1, 2, 3];
+
+ let _ = s.first();
+ // Should be replaced by s.first()
+
+ let _ = s.get(2);
+ // Should be replaced by s.get(2)
+
+ let _ = v.get(5);
+ // Should be replaced by v.get(5)
+
+ let _ = v.first();
+ // Should be replaced by v.first()
+
+ let o = Some(5);
+ o.iter().next();
+ // Shouldn't be linted since this is not a Slice or an Array
+}
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.rs b/src/tools/clippy/tests/ui/iter_next_slice.rs
new file mode 100644
index 000000000..5195f1c86
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_next_slice.rs
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::iter_next_slice)]
+
+fn main() {
+ // test code goes here
+ let s = [1, 2, 3];
+ let v = vec![1, 2, 3];
+
+ let _ = s.iter().next();
+ // Should be replaced by s.first()
+
+ let _ = s[2..].iter().next();
+ // Should be replaced by s.get(2)
+
+ let _ = v[5..].iter().next();
+ // Should be replaced by v.get(5)
+
+ let _ = v.iter().next();
+ // Should be replaced by v.first()
+
+ let o = Some(5);
+ o.iter().next();
+ // Shouldn't be linted since this is not a Slice or an Array
+}
diff --git a/src/tools/clippy/tests/ui/iter_next_slice.stderr b/src/tools/clippy/tests/ui/iter_next_slice.stderr
new file mode 100644
index 000000000..d8b89061f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_next_slice.stderr
@@ -0,0 +1,28 @@
+error: using `.iter().next()` on an array
+ --> $DIR/iter_next_slice.rs:9:13
+ |
+LL | let _ = s.iter().next();
+ | ^^^^^^^^^^^^^^^ help: try calling: `s.first()`
+ |
+ = note: `-D clippy::iter-next-slice` implied by `-D warnings`
+
+error: using `.iter().next()` on a Slice without end index
+ --> $DIR/iter_next_slice.rs:12:13
+ |
+LL | let _ = s[2..].iter().next();
+ | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)`
+
+error: using `.iter().next()` on a Slice without end index
+ --> $DIR/iter_next_slice.rs:15:13
+ |
+LL | let _ = v[5..].iter().next();
+ | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)`
+
+error: using `.iter().next()` on an array
+ --> $DIR/iter_next_slice.rs:18:13
+ |
+LL | let _ = v.iter().next();
+ | ^^^^^^^^^^^^^^^ help: try calling: `v.first()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs
new file mode 100644
index 000000000..cce216fc6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs
@@ -0,0 +1,74 @@
+#![warn(clippy::iter_not_returning_iterator)]
+
+struct Data {
+ begin: u32,
+}
+
+struct Counter {
+ count: u32,
+}
+
+impl Data {
+ fn iter(&self) -> Counter {
+ todo!()
+ }
+
+ fn iter_mut(&self) -> Counter {
+ todo!()
+ }
+}
+
+struct Data2 {
+ begin: u32,
+}
+
+struct Counter2 {
+ count: u32,
+}
+
+impl Data2 {
+ fn iter(&self) -> Counter2 {
+ todo!()
+ }
+
+ fn iter_mut(&self) -> Counter2 {
+ todo!()
+ }
+}
+
+impl Iterator for Counter {
+ type Item = u32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ todo!()
+ }
+}
+
+// Issue #8225
+trait Iter {
+ type I;
+ fn iter(&self) -> Self::I;
+}
+
+impl Iter for () {
+ type I = core::slice::Iter<'static, ()>;
+ fn iter(&self) -> Self::I {
+ [].iter()
+ }
+}
+
+struct S;
+impl S {
+ fn iter(&self) -> <() as Iter>::I {
+ ().iter()
+ }
+}
+
+struct S2([u8]);
+impl S2 {
+ fn iter(&self) -> core::slice::Iter<u8> {
+ self.0.iter()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr
new file mode 100644
index 000000000..44f029558
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr
@@ -0,0 +1,22 @@
+error: this method is named `iter` but its return type does not implement `Iterator`
+ --> $DIR/iter_not_returning_iterator.rs:30:5
+ |
+LL | fn iter(&self) -> Counter2 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings`
+
+error: this method is named `iter_mut` but its return type does not implement `Iterator`
+ --> $DIR/iter_not_returning_iterator.rs:34:5
+ |
+LL | fn iter_mut(&self) -> Counter2 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this method is named `iter` but its return type does not implement `Iterator`
+ --> $DIR/iter_not_returning_iterator.rs:50:5
+ |
+LL | fn iter(&self) -> Self::I;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_nth.rs b/src/tools/clippy/tests/ui/iter_nth.rs
new file mode 100644
index 000000000..9c21dd82e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_nth.rs
@@ -0,0 +1,56 @@
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_nth)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+use std::collections::VecDeque;
+
+/// Struct to generate false positives for things with `.iter()`.
+#[derive(Copy, Clone)]
+struct HasIter;
+
+impl HasIter {
+ fn iter(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+
+ fn iter_mut(self) -> IteratorFalsePositives {
+ IteratorFalsePositives { foo: 0 }
+ }
+}
+
+/// Checks implementation of `ITER_NTH` lint.
+fn iter_nth() {
+ let mut some_vec = vec![0, 1, 2, 3];
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect();
+
+ {
+ // Make sure we lint `.iter()` for relevant types.
+ let bad_vec = some_vec.iter().nth(3);
+ let bad_slice = &some_vec[..].iter().nth(3);
+ let bad_boxed_slice = boxed_slice.iter().nth(3);
+ let bad_vec_deque = some_vec_deque.iter().nth(3);
+ }
+
+ {
+ // Make sure we lint `.iter_mut()` for relevant types.
+ let bad_vec = some_vec.iter_mut().nth(3);
+ }
+ {
+ let bad_slice = &some_vec[..].iter_mut().nth(3);
+ }
+ {
+ let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
+ }
+
+ // Make sure we don't lint for non-relevant types.
+ let false_positive = HasIter;
+ let ok = false_positive.iter().nth(3);
+ let ok_mut = false_positive.iter_mut().nth(3);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr
new file mode 100644
index 000000000..d00b2fb67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_nth.stderr
@@ -0,0 +1,59 @@
+error: called `.iter().nth()` on a Vec
+ --> $DIR/iter_nth.rs:33:23
+ |
+LL | let bad_vec = some_vec.iter().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iter-nth` implied by `-D warnings`
+ = help: calling `.get()` is both faster and more readable
+
+error: called `.iter().nth()` on a slice
+ --> $DIR/iter_nth.rs:34:26
+ |
+LL | let bad_slice = &some_vec[..].iter().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get()` is both faster and more readable
+
+error: called `.iter().nth()` on a slice
+ --> $DIR/iter_nth.rs:35:31
+ |
+LL | let bad_boxed_slice = boxed_slice.iter().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get()` is both faster and more readable
+
+error: called `.iter().nth()` on a VecDeque
+ --> $DIR/iter_nth.rs:36:29
+ |
+LL | let bad_vec_deque = some_vec_deque.iter().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get()` is both faster and more readable
+
+error: called `.iter_mut().nth()` on a Vec
+ --> $DIR/iter_nth.rs:41:23
+ |
+LL | let bad_vec = some_vec.iter_mut().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get_mut()` is both faster and more readable
+
+error: called `.iter_mut().nth()` on a slice
+ --> $DIR/iter_nth.rs:44:26
+ |
+LL | let bad_slice = &some_vec[..].iter_mut().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get_mut()` is both faster and more readable
+
+error: called `.iter_mut().nth()` on a VecDeque
+ --> $DIR/iter_nth.rs:47:29
+ |
+LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: calling `.get_mut()` is both faster and more readable
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.fixed b/src/tools/clippy/tests/ui/iter_nth_zero.fixed
new file mode 100644
index 000000000..f23671c26
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_nth_zero.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+
+#![warn(clippy::iter_nth_zero)]
+use std::collections::HashSet;
+
+struct Foo;
+
+impl Foo {
+ fn nth(&self, index: usize) -> usize {
+ index + 1
+ }
+}
+
+fn main() {
+ let f = Foo {};
+ f.nth(0); // lint does not apply here
+
+ let mut s = HashSet::new();
+ s.insert(1);
+ let _x = s.iter().next();
+
+ let mut s2 = HashSet::new();
+ s2.insert(2);
+ let mut iter = s2.iter();
+ let _y = iter.next();
+
+ let mut s3 = HashSet::new();
+ s3.insert(3);
+ let mut iter2 = s3.iter();
+ let _unwrapped = iter2.next().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.rs b/src/tools/clippy/tests/ui/iter_nth_zero.rs
new file mode 100644
index 000000000..7c968d498
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_nth_zero.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+
+#![warn(clippy::iter_nth_zero)]
+use std::collections::HashSet;
+
+struct Foo;
+
+impl Foo {
+ fn nth(&self, index: usize) -> usize {
+ index + 1
+ }
+}
+
+fn main() {
+ let f = Foo {};
+ f.nth(0); // lint does not apply here
+
+ let mut s = HashSet::new();
+ s.insert(1);
+ let _x = s.iter().nth(0);
+
+ let mut s2 = HashSet::new();
+ s2.insert(2);
+ let mut iter = s2.iter();
+ let _y = iter.nth(0);
+
+ let mut s3 = HashSet::new();
+ s3.insert(3);
+ let mut iter2 = s3.iter();
+ let _unwrapped = iter2.nth(0).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.stderr b/src/tools/clippy/tests/ui/iter_nth_zero.stderr
new file mode 100644
index 000000000..29c56f3a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_nth_zero.stderr
@@ -0,0 +1,22 @@
+error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent
+ --> $DIR/iter_nth_zero.rs:20:14
+ |
+LL | let _x = s.iter().nth(0);
+ | ^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `s.iter().next()`
+ |
+ = note: `-D clippy::iter-nth-zero` implied by `-D warnings`
+
+error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent
+ --> $DIR/iter_nth_zero.rs:25:14
+ |
+LL | let _y = iter.nth(0);
+ | ^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter.next()`
+
+error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent
+ --> $DIR/iter_nth_zero.rs:30:22
+ |
+LL | let _unwrapped = iter2.nth(0).unwrap();
+ | ^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter2.next()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
new file mode 100644
index 000000000..c100705d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
@@ -0,0 +1,55 @@
+// run-rustfix
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code, clippy::let_unit_value)]
+
+fn main() {
+ let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
+
+ let _: Option<String> = vec.iter().last().cloned();
+
+ let _: Option<String> = vec.iter().chain(vec.iter()).next().cloned();
+
+ let _: usize = vec.iter().filter(|x| x == &"2").count();
+
+ let _: Vec<_> = vec.iter().take(2).cloned().collect();
+
+ let _: Vec<_> = vec.iter().skip(2).cloned().collect();
+
+ let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned();
+
+ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ .iter()
+ .flatten().cloned();
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().map(|x| x.len());
+
+ // This would fail if changed.
+ let _ = vec.iter().cloned().map(|x| x + "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().find(|x| x == "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().all(|x| x.len() == 1);
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().any(|x| x.len() == 1);
+
+ // Should probably stay as it is.
+ let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
+
+ // `&Range<_>` doesn't implement `IntoIterator`
+ let _ = [0..1, 2..5].iter().cloned().flatten();
+}
+
+// #8527
+fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
+ x.cloned().flatten()
+}
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
new file mode 100644
index 000000000..2caa88020
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code, clippy::let_unit_value)]
+
+fn main() {
+ let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
+
+ let _: Option<String> = vec.iter().cloned().last();
+
+ let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
+
+ let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
+
+ let _: Vec<_> = vec.iter().cloned().take(2).collect();
+
+ let _: Vec<_> = vec.iter().cloned().skip(2).collect();
+
+ let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
+
+ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ .iter()
+ .cloned()
+ .flatten();
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().map(|x| x.len());
+
+ // This would fail if changed.
+ let _ = vec.iter().cloned().map(|x| x + "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().find(|x| x == "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().all(|x| x.len() == 1);
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().any(|x| x.len() == 1);
+
+ // Should probably stay as it is.
+ let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
+
+ // `&Range<_>` doesn't implement `IntoIterator`
+ let _ = [0..1, 2..5].iter().cloned().flatten();
+}
+
+// #8527
+fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
+ x.cloned().flatten()
+}
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
new file mode 100644
index 000000000..dcae7cecd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
@@ -0,0 +1,70 @@
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:8:29
+ |
+LL | let _: Option<String> = vec.iter().cloned().last();
+ | ^^^^^^^^^^----------------
+ | |
+ | help: try this: `.last().cloned()`
+ |
+ = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings`
+
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:10:29
+ |
+LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------
+ | |
+ | help: try this: `.next().cloned()`
+
+error: unneeded cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:12:20
+ |
+LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------
+ | |
+ | help: try this: `.count()`
+ |
+ = note: `-D clippy::redundant-clone` implied by `-D warnings`
+
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:14:21
+ |
+LL | let _: Vec<_> = vec.iter().cloned().take(2).collect();
+ | ^^^^^^^^^^-----------------
+ | |
+ | help: try this: `.take(2).cloned()`
+
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:16:21
+ |
+LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect();
+ | ^^^^^^^^^^-----------------
+ | |
+ | help: try this: `.skip(2).cloned()`
+
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:18:13
+ |
+LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------
+ | |
+ | help: try this: `.nth(2).cloned()`
+
+error: unnecessarily eager cloning of iterator items
+ --> $DIR/iter_overeager_cloned.rs:20:13
+ |
+LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ | _____________^
+LL | | .iter()
+LL | | .cloned()
+LL | | .flatten();
+ | |__________________^
+ |
+help: try this
+ |
+LL ~ .iter()
+LL ~ .flatten().cloned();
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed
new file mode 100644
index 000000000..2db4c2bee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_skip_next.fixed
@@ -0,0 +1,37 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_skip_next)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::iter_nth)]
+#![allow(unused_mut, dead_code)]
+
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+
+/// Checks implementation of `ITER_SKIP_NEXT` lint
+fn main() {
+ let some_vec = vec![0, 1, 2, 3];
+ let _ = some_vec.iter().nth(42);
+ let _ = some_vec.iter().cycle().nth(42);
+ let _ = (1..10).nth(10);
+ let _ = &some_vec[..].iter().nth(3);
+ let foo = IteratorFalsePositives { foo: 0 };
+ let _ = foo.skip(42).next();
+ let _ = foo.filter().skip(42).next();
+
+ // fix #8128
+ let test_string = "1|1 2";
+ let mut sp = test_string.split('|').map(|s| s.trim());
+ let _: Vec<&str> = sp.nth(1).unwrap().split(' ').collect();
+ if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) {
+ let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect();
+ };
+ fn check<T>(mut s: T)
+ where
+ T: Iterator<Item = String>,
+ {
+ let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs
new file mode 100644
index 000000000..692edb9ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_skip_next.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_skip_next)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::iter_nth)]
+#![allow(unused_mut, dead_code)]
+
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+
+/// Checks implementation of `ITER_SKIP_NEXT` lint
+fn main() {
+ let some_vec = vec![0, 1, 2, 3];
+ let _ = some_vec.iter().skip(42).next();
+ let _ = some_vec.iter().cycle().skip(42).next();
+ let _ = (1..10).skip(10).next();
+ let _ = &some_vec[..].iter().skip(3).next();
+ let foo = IteratorFalsePositives { foo: 0 };
+ let _ = foo.skip(42).next();
+ let _ = foo.filter().skip(42).next();
+
+ // fix #8128
+ let test_string = "1|1 2";
+ let mut sp = test_string.split('|').map(|s| s.trim());
+ let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
+ if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) {
+ let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ };
+ fn check<T>(mut s: T)
+ where
+ T: Iterator<Item = String>,
+ {
+ let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr
new file mode 100644
index 000000000..ca6970b27
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr
@@ -0,0 +1,46 @@
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:16:28
+ |
+LL | let _ = some_vec.iter().skip(42).next();
+ | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
+ |
+ = note: `-D clippy::iter-skip-next` implied by `-D warnings`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:17:36
+ |
+LL | let _ = some_vec.iter().cycle().skip(42).next();
+ | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:18:20
+ |
+LL | let _ = (1..10).skip(10).next();
+ | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:19:33
+ |
+LL | let _ = &some_vec[..].iter().skip(3).next();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:27:26
+ |
+LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:29:29
+ |
+LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next.rs:35:29
+ |
+LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs
new file mode 100644
index 000000000..3607330cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs
@@ -0,0 +1,19 @@
+#![warn(clippy::iter_skip_next)]
+#![allow(dead_code)]
+
+/// Checks implementation of `ITER_SKIP_NEXT` lint
+fn main() {
+ // fix #8128
+ let test_string = "1|1 2";
+ let sp = test_string.split('|').map(|s| s.trim());
+ let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
+ if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) {
+ let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ };
+ fn check<T>(s: T)
+ where
+ T: Iterator<Item = String>,
+ {
+ let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/iter_skip_next_unfixable.stderr b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.stderr
new file mode 100644
index 000000000..74c327c74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.stderr
@@ -0,0 +1,39 @@
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next_unfixable.rs:9:26
+ |
+LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+ |
+ = note: `-D clippy::iter-skip-next` implied by `-D warnings`
+help: for this change `sp` has to be mutable
+ --> $DIR/iter_skip_next_unfixable.rs:8:9
+ |
+LL | let sp = test_string.split('|').map(|s| s.trim());
+ | ^^
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next_unfixable.rs:11:29
+ |
+LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+ |
+help: for this change `s` has to be mutable
+ --> $DIR/iter_skip_next_unfixable.rs:10:17
+ |
+LL | if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) {
+ | ^
+
+error: called `skip(..).next()` on an iterator
+ --> $DIR/iter_skip_next_unfixable.rs:17:29
+ |
+LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
+ | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
+ |
+help: for this change `s` has to be mutable
+ --> $DIR/iter_skip_next_unfixable.rs:13:17
+ |
+LL | fn check<T>(s: T)
+ | ^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_with_drain.fixed b/src/tools/clippy/tests/ui/iter_with_drain.fixed
new file mode 100644
index 000000000..0330d5549
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_with_drain.fixed
@@ -0,0 +1,65 @@
+// run-rustfix
+// will emits unused mut warnings after fixing
+#![allow(unused_mut)]
+// will emits needless collect warnings after fixing
+#![allow(clippy::needless_collect)]
+#![warn(clippy::iter_with_drain)]
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn full() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.into_iter().collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.into_iter().collect();
+ let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+}
+
+fn closed() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.into_iter().collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.into_iter().collect();
+ let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+}
+
+fn should_not_help() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.drain(1..).collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
+ let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+
+ let mut b = vec!["aaa".to_string(), "bbb".to_string()];
+ let _: Vec<_> = b.drain(0..a.len()).collect();
+}
+
+fn _closed_range(mut x: Vec<String>) {
+ let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+ let _: Vec<String> = x.drain(..).collect();
+ let _: Vec<String> = y.drain(..).collect();
+}
+
+#[derive(Default)]
+struct Bomb {
+ fire: Vec<u8>,
+}
+
+fn should_not_help_0(bomb: &mut Bomb) {
+ let _: Vec<u8> = bomb.fire.drain(..).collect();
+}
+
+fn main() {
+ full();
+ closed();
+ should_not_help();
+ should_not_help_0(&mut Bomb::default());
+}
diff --git a/src/tools/clippy/tests/ui/iter_with_drain.rs b/src/tools/clippy/tests/ui/iter_with_drain.rs
new file mode 100644
index 000000000..993936fb8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_with_drain.rs
@@ -0,0 +1,65 @@
+// run-rustfix
+// will emits unused mut warnings after fixing
+#![allow(unused_mut)]
+// will emits needless collect warnings after fixing
+#![allow(clippy::needless_collect)]
+#![warn(clippy::iter_with_drain)]
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn full() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.drain(..).collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.drain(..).collect();
+ let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+}
+
+fn closed() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.drain(0..).collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.drain(..a.len()).collect();
+ let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+}
+
+fn should_not_help() {
+ let mut a = vec!["aaa".to_string(), "bbb".to_string()];
+ let mut a: BinaryHeap<_> = a.drain(1..).collect();
+ let mut a: HashSet<_> = a.drain().collect();
+ let mut a: VecDeque<_> = a.drain().collect();
+ let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
+ let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
+ let _: Vec<(String, String)> = a.drain().collect();
+
+ let mut b = vec!["aaa".to_string(), "bbb".to_string()];
+ let _: Vec<_> = b.drain(0..a.len()).collect();
+}
+
+fn _closed_range(mut x: Vec<String>) {
+ let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+ let _: Vec<String> = x.drain(..).collect();
+ let _: Vec<String> = y.drain(..).collect();
+}
+
+#[derive(Default)]
+struct Bomb {
+ fire: Vec<u8>,
+}
+
+fn should_not_help_0(bomb: &mut Bomb) {
+ let _: Vec<u8> = bomb.fire.drain(..).collect();
+}
+
+fn main() {
+ full();
+ closed();
+ should_not_help();
+ should_not_help_0(&mut Bomb::default());
+}
diff --git a/src/tools/clippy/tests/ui/iter_with_drain.stderr b/src/tools/clippy/tests/ui/iter_with_drain.stderr
new file mode 100644
index 000000000..aa394439f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_with_drain.stderr
@@ -0,0 +1,40 @@
+error: `drain(..)` used on a `Vec`
+ --> $DIR/iter_with_drain.rs:11:34
+ |
+LL | let mut a: BinaryHeap<_> = a.drain(..).collect();
+ | ^^^^^^^^^ help: try this: `into_iter()`
+ |
+ = note: `-D clippy::iter-with-drain` implied by `-D warnings`
+
+error: `drain(..)` used on a `VecDeque`
+ --> $DIR/iter_with_drain.rs:14:27
+ |
+LL | let mut a: Vec<_> = a.drain(..).collect();
+ | ^^^^^^^^^ help: try this: `into_iter()`
+
+error: `drain(..)` used on a `Vec`
+ --> $DIR/iter_with_drain.rs:15:34
+ |
+LL | let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect();
+ | ^^^^^^^^^ help: try this: `into_iter()`
+
+error: `drain(..)` used on a `Vec`
+ --> $DIR/iter_with_drain.rs:21:34
+ |
+LL | let mut a: BinaryHeap<_> = a.drain(0..).collect();
+ | ^^^^^^^^^^ help: try this: `into_iter()`
+
+error: `drain(..)` used on a `VecDeque`
+ --> $DIR/iter_with_drain.rs:24:27
+ |
+LL | let mut a: Vec<_> = a.drain(..a.len()).collect();
+ | ^^^^^^^^^^^^^^^^ help: try this: `into_iter()`
+
+error: `drain(..)` used on a `Vec`
+ --> $DIR/iter_with_drain.rs:25:34
+ |
+LL | let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect();
+ | ^^^^^^^^^^^^^^^^^ help: try this: `into_iter()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.rs b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs
new file mode 100644
index 000000000..13d1cfd42
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs
@@ -0,0 +1,28 @@
+#[warn(clippy::iterator_step_by_zero)]
+fn main() {
+ let _ = vec!["A", "B", "B"].iter().step_by(0);
+ let _ = "XXX".chars().step_by(0);
+ let _ = (0..1).step_by(0);
+
+ // No error, not an iterator.
+ let y = NotIterator;
+ y.step_by(0);
+
+ // No warning for non-zero step
+ let _ = (0..1).step_by(1);
+
+ let _ = (1..).step_by(0);
+ let _ = (1..=2).step_by(0);
+
+ let x = 0..1;
+ let _ = x.step_by(0);
+
+ // check const eval
+ let v1 = vec![1, 2, 3];
+ let _ = v1.iter().step_by(2 / 3);
+}
+
+struct NotIterator;
+impl NotIterator {
+ fn step_by(&self, _: u32) {}
+}
diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
new file mode 100644
index 000000000..d792aea11
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
@@ -0,0 +1,46 @@
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:3:13
+ |
+LL | let _ = vec!["A", "B", "B"].iter().step_by(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings`
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:4:13
+ |
+LL | let _ = "XXX".chars().step_by(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:5:13
+ |
+LL | let _ = (0..1).step_by(0);
+ | ^^^^^^^^^^^^^^^^^
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:14:13
+ |
+LL | let _ = (1..).step_by(0);
+ | ^^^^^^^^^^^^^^^^
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:15:13
+ |
+LL | let _ = (1..=2).step_by(0);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:18:13
+ |
+LL | let _ = x.step_by(0);
+ | ^^^^^^^^^^^^
+
+error: `Iterator::step_by(0)` will panic at runtime
+ --> $DIR/iterator_step_by_zero.rs:22:13
+ |
+LL | let _ = v1.iter().step_by(2 / 3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed
new file mode 100644
index 000000000..c5af07c8a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::large_const_arrays)]
+#![allow(dead_code)]
+
+#[derive(Clone, Copy)]
+pub struct S {
+ pub data: [u64; 32],
+}
+
+// Should lint
+pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
+pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
+
+// Good
+pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
+pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
+const G_FOO: [u32; 1_000] = [0u32; 1_000];
+
+fn main() {
+ // Should lint
+ pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+ static BAR: [u32; 1_000_000] = [0u32; 1_000_000];
+ pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
+ static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
+
+ // Good
+ pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
+ const G_BAR: [u32; 1_000] = [0u32; 1_000];
+ pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
+ const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
+ pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
+ const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
+}
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs
new file mode 100644
index 000000000..a160b9f8a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_const_arrays.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::large_const_arrays)]
+#![allow(dead_code)]
+
+#[derive(Clone, Copy)]
+pub struct S {
+ pub data: [u64; 32],
+}
+
+// Should lint
+pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
+pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
+
+// Good
+pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
+pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
+const G_FOO: [u32; 1_000] = [0u32; 1_000];
+
+fn main() {
+ // Should lint
+ pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+ const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
+ pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
+ const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
+
+ // Good
+ pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
+ const G_BAR: [u32; 1_000] = [0u32; 1_000];
+ pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
+ const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
+ pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
+ const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
+}
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.stderr b/src/tools/clippy/tests/ui/large_const_arrays.stderr
new file mode 100644
index 000000000..3fb0acbca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_const_arrays.stderr
@@ -0,0 +1,76 @@
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:12:1
+ |
+LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
+ | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+ |
+ = note: `-D clippy::large-const-arrays` implied by `-D warnings`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:13:1
+ |
+LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:14:1
+ |
+LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:23:5
+ |
+LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:24:5
+ |
+LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:25:5
+ |
+LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:26:5
+ |
+LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:27:5
+ |
+LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
+ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: large array defined as const
+ --> $DIR/large_const_arrays.rs:28:5
+ |
+LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_digit_groups.fixed b/src/tools/clippy/tests/ui/large_digit_groups.fixed
new file mode 100644
index 000000000..3430c137e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_digit_groups.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+#![warn(clippy::large_digit_groups)]
+
+fn main() {
+ macro_rules! mac {
+ () => {
+ 0b1_10110_i64
+ };
+ }
+
+ let _good = (
+ 0b1011_i64,
+ 0o1_234_u32,
+ 0x0123_4567,
+ 1_2345_6789,
+ 1234_f32,
+ 1_234.12_f32,
+ 1_234.123_f32,
+ 1.123_4_f32,
+ );
+ let _bad = (
+ 0b11_0110_i64,
+ 0xdead_beef_usize,
+ 123_456_f32,
+ 123_456.12_f32,
+ 123_456.123_45_f64,
+ 123_456.123_456_f64,
+ );
+ // Ignore literals in macros
+ let _ = mac!();
+}
diff --git a/src/tools/clippy/tests/ui/large_digit_groups.rs b/src/tools/clippy/tests/ui/large_digit_groups.rs
new file mode 100644
index 000000000..ac116d5db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_digit_groups.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+#![warn(clippy::large_digit_groups)]
+
+fn main() {
+ macro_rules! mac {
+ () => {
+ 0b1_10110_i64
+ };
+ }
+
+ let _good = (
+ 0b1011_i64,
+ 0o1_234_u32,
+ 0x1_234_567,
+ 1_2345_6789,
+ 1234_f32,
+ 1_234.12_f32,
+ 1_234.123_f32,
+ 1.123_4_f32,
+ );
+ let _bad = (
+ 0b1_10110_i64,
+ 0xd_e_adbee_f_usize,
+ 1_23456_f32,
+ 1_23456.12_f32,
+ 1_23456.12345_f64,
+ 1_23456.12345_6_f64,
+ );
+ // Ignore literals in macros
+ let _ = mac!();
+}
diff --git a/src/tools/clippy/tests/ui/large_digit_groups.stderr b/src/tools/clippy/tests/ui/large_digit_groups.stderr
new file mode 100644
index 000000000..13d108b56
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_digit_groups.stderr
@@ -0,0 +1,48 @@
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/large_digit_groups.rs:14:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/large_digit_groups.rs:22:9
+ |
+LL | 0b1_10110_i64,
+ | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/large_digit_groups.rs:23:9
+ |
+LL | 0xd_e_adbee_f_usize,
+ | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
+
+error: digit groups should be smaller
+ --> $DIR/large_digit_groups.rs:24:9
+ |
+LL | 1_23456_f32,
+ | ^^^^^^^^^^^ help: consider: `123_456_f32`
+ |
+ = note: `-D clippy::large-digit-groups` implied by `-D warnings`
+
+error: digit groups should be smaller
+ --> $DIR/large_digit_groups.rs:25:9
+ |
+LL | 1_23456.12_f32,
+ | ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32`
+
+error: digit groups should be smaller
+ --> $DIR/large_digit_groups.rs:26:9
+ |
+LL | 1_23456.12345_f64,
+ | ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64`
+
+error: digit groups should be smaller
+ --> $DIR/large_digit_groups.rs:27:9
+ |
+LL | 1_23456.12345_6_f64,
+ | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs
new file mode 100644
index 000000000..23152a133
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_enum_variant.rs
@@ -0,0 +1,135 @@
+// aux-build:macro_rules.rs
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![warn(clippy::large_enum_variant)]
+
+#[macro_use]
+extern crate macro_rules;
+
+enum LargeEnum {
+ A(i32),
+ B([i32; 8000]),
+}
+
+enum GenericEnumOk<T> {
+ A(i32),
+ B([T; 8000]),
+}
+
+enum GenericEnum2<T> {
+ A(i32),
+ B([i32; 8000]),
+ C(T, [i32; 8000]),
+}
+
+trait SomeTrait {
+ type Item;
+}
+
+enum LargeEnumGeneric<A: SomeTrait> {
+ Var(A::Item),
+}
+
+enum LargeEnum2 {
+ VariantOk(i32, u32),
+ ContainingLargeEnum(LargeEnum),
+}
+
+enum LargeEnum3 {
+ ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
+ VoidVariant,
+ StructLikeLittle { x: i32, y: i32 },
+}
+
+enum LargeEnum4 {
+ VariantOk(i32, u32),
+ StructLikeLarge { x: [i32; 8000], y: i32 },
+}
+
+enum LargeEnum5 {
+ VariantOk(i32, u32),
+ StructLikeLarge2 { x: [i32; 8000] },
+}
+
+enum LargeEnumOk {
+ LargeA([i32; 8000]),
+ LargeB([i32; 8001]),
+}
+
+enum LargeEnum6 {
+ A,
+ B([u8; 255]),
+ C([u8; 200]),
+}
+
+enum LargeEnum7 {
+ A,
+ B([u8; 1255]),
+ C([u8; 200]),
+}
+
+enum LargeEnum8 {
+ VariantOk(i32, u32),
+ ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
+}
+
+enum LargeEnum9 {
+ A(Struct<()>),
+ B(Struct2),
+}
+
+enum LargeEnumOk2<T> {
+ A(T),
+ B(Struct2),
+}
+
+enum LargeEnumOk3<T> {
+ A(Struct<T>),
+ B(Struct2),
+}
+
+struct Struct<T> {
+ a: i32,
+ t: T,
+}
+
+struct Struct2 {
+ a: [i32; 8000],
+}
+
+#[derive(Copy, Clone)]
+enum CopyableLargeEnum {
+ A(bool),
+ B([u128; 4000]),
+}
+
+enum ManuallyCopyLargeEnum {
+ A(bool),
+ B([u128; 4000]),
+}
+
+impl Clone for ManuallyCopyLargeEnum {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl Copy for ManuallyCopyLargeEnum {}
+
+enum SomeGenericPossiblyCopyEnum<T> {
+ A(bool, std::marker::PhantomData<T>),
+ B([u64; 4000]),
+}
+
+impl<T: Copy> Clone for SomeGenericPossiblyCopyEnum<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T: Copy> Copy for SomeGenericPossiblyCopyEnum<T> {}
+
+fn main() {
+ large_enum_variant!();
+}
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr
new file mode 100644
index 000000000..024832726
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr
@@ -0,0 +1,197 @@
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:12:5
+ |
+LL | B([i32; 8000]),
+ | ^^^^^^^^^^^^^^ this variant is 32000 bytes
+ |
+ = note: `-D clippy::large-enum-variant` implied by `-D warnings`
+note: and the second-largest variant is 4 bytes:
+ --> $DIR/large_enum_variant.rs:11:5
+ |
+LL | A(i32),
+ | ^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | B(Box<[i32; 8000]>),
+ | ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:36:5
+ |
+LL | ContainingLargeEnum(LargeEnum),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:35:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | ContainingLargeEnum(Box<LargeEnum>),
+ | ~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:40:5
+ |
+LL | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70004 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:42:5
+ |
+LL | StructLikeLittle { x: i32, y: i32 },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
+ | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:47:5
+ |
+LL | StructLikeLarge { x: [i32; 8000], y: i32 },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:46:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
+ | ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:52:5
+ |
+LL | StructLikeLarge2 { x: [i32; 8000] },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:51:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | StructLikeLarge2 { x: Box<[i32; 8000]> },
+ | ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:68:5
+ |
+LL | B([u8; 1255]),
+ | ^^^^^^^^^^^^^ this variant is 1255 bytes
+ |
+note: and the second-largest variant is 200 bytes:
+ --> $DIR/large_enum_variant.rs:69:5
+ |
+LL | C([u8; 200]),
+ | ^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | B(Box<[u8; 1255]>),
+ | ~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:74:5
+ |
+LL | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70128 bytes
+ |
+note: and the second-largest variant is 8 bytes:
+ --> $DIR/large_enum_variant.rs:73:5
+ |
+LL | VariantOk(i32, u32),
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
+ | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:79:5
+ |
+LL | B(Struct2),
+ | ^^^^^^^^^^ this variant is 32000 bytes
+ |
+note: and the second-largest variant is 4 bytes:
+ --> $DIR/large_enum_variant.rs:78:5
+ |
+LL | A(Struct<()>),
+ | ^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ |
+LL | B(Box<Struct2>),
+ | ~~~~~~~~~~~~
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:104:5
+ |
+LL | B([u128; 4000]),
+ | ^^^^^^^^^^^^^^^ this variant is 64000 bytes
+ |
+note: and the second-largest variant is 1 bytes:
+ --> $DIR/large_enum_variant.rs:103:5
+ |
+LL | A(bool),
+ | ^^^^^^^
+note: boxing a variant would require the type no longer be `Copy`
+ --> $DIR/large_enum_variant.rs:102:6
+ |
+LL | enum CopyableLargeEnum {
+ | ^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ --> $DIR/large_enum_variant.rs:104:5
+ |
+LL | B([u128; 4000]),
+ | ^^^^^^^^^^^^^^^
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:109:5
+ |
+LL | B([u128; 4000]),
+ | ^^^^^^^^^^^^^^^ this variant is 64000 bytes
+ |
+note: and the second-largest variant is 1 bytes:
+ --> $DIR/large_enum_variant.rs:108:5
+ |
+LL | A(bool),
+ | ^^^^^^^
+note: boxing a variant would require the type no longer be `Copy`
+ --> $DIR/large_enum_variant.rs:107:6
+ |
+LL | enum ManuallyCopyLargeEnum {
+ | ^^^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ --> $DIR/large_enum_variant.rs:109:5
+ |
+LL | B([u128; 4000]),
+ | ^^^^^^^^^^^^^^^
+
+error: large size difference between variants
+ --> $DIR/large_enum_variant.rs:122:5
+ |
+LL | B([u64; 4000]),
+ | ^^^^^^^^^^^^^^ this variant is 32000 bytes
+ |
+note: and the second-largest variant is 1 bytes:
+ --> $DIR/large_enum_variant.rs:121:5
+ |
+LL | A(bool, std::marker::PhantomData<T>),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: boxing a variant would require the type no longer be `Copy`
+ --> $DIR/large_enum_variant.rs:120:6
+ |
+LL | enum SomeGenericPossiblyCopyEnum<T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+ --> $DIR/large_enum_variant.rs:122:5
+ |
+LL | B([u64; 4000]),
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs
new file mode 100644
index 000000000..d9161bfcf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs
@@ -0,0 +1,30 @@
+#![warn(clippy::large_stack_arrays)]
+#![allow(clippy::large_enum_variant)]
+
+#[derive(Clone, Copy)]
+struct S {
+ pub data: [u64; 32],
+}
+
+#[derive(Clone, Copy)]
+enum E {
+ S(S),
+ T(u32),
+}
+
+fn main() {
+ let bad = (
+ [0u32; 20_000_000],
+ [S { data: [0; 32] }; 5000],
+ [Some(""); 20_000_000],
+ [E::T(0); 5000],
+ );
+
+ let good = (
+ [0u32; 1000],
+ [S { data: [0; 32] }; 1000],
+ [Some(""); 1000],
+ [E::T(0); 1000],
+ [(); 20_000_000],
+ );
+}
diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
new file mode 100644
index 000000000..58c0a77c1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr
@@ -0,0 +1,35 @@
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:17:9
+ |
+LL | [0u32; 20_000_000],
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
+ = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
+
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:18:9
+ |
+LL | [S { data: [0; 32] }; 5000],
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
+
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:19:9
+ |
+LL | [Some(""); 20_000_000],
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
+
+error: allocating a local array larger than 512000 bytes
+ --> $DIR/large_stack_arrays.rs:20:9
+ |
+LL | [E::T(0); 5000],
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_types_passed_by_value.rs b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs
new file mode 100644
index 000000000..7601b5c66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs
@@ -0,0 +1,66 @@
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![warn(clippy::large_types_passed_by_value)]
+
+pub struct Large([u8; 2048]);
+
+#[derive(Clone, Copy)]
+pub struct LargeAndCopy([u8; 2048]);
+
+pub struct Small([u8; 4]);
+
+#[derive(Clone, Copy)]
+pub struct SmallAndCopy([u8; 4]);
+
+fn small(a: Small, b: SmallAndCopy) {}
+fn not_copy(a: Large) {}
+fn by_ref(a: &Large, b: &LargeAndCopy) {}
+fn mutable(mut a: LargeAndCopy) {}
+fn bad(a: LargeAndCopy) {}
+pub fn bad_but_pub(a: LargeAndCopy) {}
+
+impl LargeAndCopy {
+ fn self_is_ok(self) {}
+ fn other_is_not_ok(self, other: LargeAndCopy) {}
+ fn unless_other_can_change(self, mut other: LargeAndCopy) {}
+ pub fn or_were_in_public(self, other: LargeAndCopy) {}
+}
+
+trait LargeTypeDevourer {
+ fn devoure_array(&self, array: [u8; 6666]);
+ fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
+ fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+}
+
+pub trait PubLargeTypeDevourer {
+ fn devoure_array_in_public(&self, array: [u8; 6666]);
+}
+
+struct S;
+impl LargeTypeDevourer for S {
+ fn devoure_array(&self, array: [u8; 6666]) {
+ todo!();
+ }
+ fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) {
+ todo!();
+ }
+ fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) {
+ todo!();
+ }
+}
+
+#[inline(always)]
+fn foo_always(x: LargeAndCopy) {
+ todo!();
+}
+#[inline(never)]
+fn foo_never(x: LargeAndCopy) {
+ todo!();
+}
+#[inline]
+fn foo(x: LargeAndCopy) {
+ todo!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr b/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr
new file mode 100644
index 000000000..5f42dcfb9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr
@@ -0,0 +1,52 @@
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:20:11
+ |
+LL | fn bad(a: LargeAndCopy) {}
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+ |
+ = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:25:37
+ |
+LL | fn other_is_not_ok(self, other: LargeAndCopy) {}
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:31:36
+ |
+LL | fn devoure_array(&self, array: [u8; 6666]);
+ | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:32:34
+ |
+LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:33:50
+ |
+LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:33:67
+ |
+LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:58:17
+ |
+LL | fn foo_never(x: LargeAndCopy) {
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:62:11
+ |
+LL | fn foo(x: LargeAndCopy) {
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs
new file mode 100644
index 000000000..1e938e72b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs
@@ -0,0 +1,285 @@
+#![warn(clippy::len_without_is_empty)]
+#![allow(dead_code, unused)]
+
+pub struct PubOne;
+
+impl PubOne {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+impl PubOne {
+ // A second impl for this struct -- the error span shouldn't mention this.
+ pub fn irrelevant(&self) -> bool {
+ false
+ }
+}
+
+// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`.
+pub struct PubAllowed;
+
+#[allow(clippy::len_without_is_empty)]
+impl PubAllowed {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
+// impl containing `len`.
+impl PubAllowed {
+ pub fn irrelevant(&self) -> bool {
+ false
+ }
+}
+
+pub struct PubAllowedFn;
+
+impl PubAllowedFn {
+ #[allow(clippy::len_without_is_empty)]
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+#[allow(clippy::len_without_is_empty)]
+pub struct PubAllowedStruct;
+
+impl PubAllowedStruct {
+ pub fn len(&self) -> isize {
+ 1
+ }
+}
+
+pub trait PubTraitsToo {
+ fn len(&self) -> isize;
+}
+
+impl PubTraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+pub struct HasIsEmpty;
+
+impl HasIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub struct HasWrongIsEmpty;
+
+impl HasWrongIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self, x: u32) -> bool {
+ false
+ }
+}
+
+pub struct MismatchedSelf;
+
+impl MismatchedSelf {
+ pub fn len(self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+struct NotPubOne;
+
+impl NotPubOne {
+ pub fn len(&self) -> isize {
+ // No error; `len` is pub but `NotPubOne` is not exported anyway.
+ 1
+ }
+}
+
+struct One;
+
+impl One {
+ fn len(&self) -> isize {
+ // No error; `len` is private; see issue #1085.
+ 1
+ }
+}
+
+trait TraitsToo {
+ fn len(&self) -> isize;
+ // No error; `len` is private; see issue #1085.
+}
+
+impl TraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+struct HasPrivateIsEmpty;
+
+impl HasPrivateIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+struct Wither;
+
+pub trait WithIsEmpty {
+ fn len(&self) -> isize;
+ fn is_empty(&self) -> bool;
+}
+
+impl WithIsEmpty for Wither {
+ fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub trait Empty {
+ fn is_empty(&self) -> bool;
+}
+
+pub trait InheritingEmpty: Empty {
+ // Must not trigger `LEN_WITHOUT_IS_EMPTY`.
+ fn len(&self) -> isize;
+}
+
+// This used to ICE.
+pub trait Foo: Sized {}
+
+pub trait DependsOnFoo: Foo {
+ fn len(&mut self) -> usize;
+}
+
+// issue #1562
+pub struct MultipleImpls;
+
+impl MultipleImpls {
+ pub fn len(&self) -> usize {
+ 1
+ }
+}
+
+impl MultipleImpls {
+ pub fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+// issue #6958
+pub struct OptionalLen;
+
+impl OptionalLen {
+ pub fn len(&self) -> Option<usize> {
+ Some(0)
+ }
+
+ pub fn is_empty(&self) -> Option<bool> {
+ Some(true)
+ }
+}
+
+pub struct OptionalLen2;
+impl OptionalLen2 {
+ pub fn len(&self) -> Option<usize> {
+ Some(0)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+pub struct OptionalLen3;
+impl OptionalLen3 {
+ pub fn len(&self) -> usize {
+ 0
+ }
+
+ // should lint, len is not an option
+ pub fn is_empty(&self) -> Option<bool> {
+ None
+ }
+}
+
+pub struct ResultLen;
+impl ResultLen {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ // Differing result types
+ pub fn is_empty(&self) -> Option<bool> {
+ Some(true)
+ }
+}
+
+pub struct ResultLen2;
+impl ResultLen2 {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ pub fn is_empty(&self) -> Result<bool, ()> {
+ Ok(true)
+ }
+}
+
+pub struct ResultLen3;
+impl ResultLen3 {
+ pub fn len(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+
+ // Non-fallible result is ok.
+ pub fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+pub struct OddLenSig;
+impl OddLenSig {
+ // don't lint
+ pub fn len(&self) -> bool {
+ true
+ }
+}
+
+// issue #6958
+pub struct AsyncLen;
+impl AsyncLen {
+ async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> usize {
+ if self.async_task().await { 0 } else { 1 }
+ }
+
+ pub async fn is_empty(&self) -> bool {
+ self.len().await == 0
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
new file mode 100644
index 000000000..a1f48f761
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
@@ -0,0 +1,123 @@
+error: struct `PubOne` has a public `len` method, but no `is_empty` method
+ --> $DIR/len_without_is_empty.rs:7:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::len-without-is-empty` implied by `-D warnings`
+
+error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method
+ --> $DIR/len_without_is_empty.rs:55:1
+ |
+LL | / pub trait PubTraitsToo {
+LL | | fn len(&self) -> isize;
+LL | | }
+ | |_^
+
+error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method
+ --> $DIR/len_without_is_empty.rs:68:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
+ --> $DIR/len_without_is_empty.rs:72:5
+ |
+LL | fn is_empty(&self) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature
+ --> $DIR/len_without_is_empty.rs:80:5
+ |
+LL | pub fn len(&self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
+ --> $DIR/len_without_is_empty.rs:84:5
+ |
+LL | pub fn is_empty(&self, x: u32) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool`
+
+error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature
+ --> $DIR/len_without_is_empty.rs:92:5
+ |
+LL | pub fn len(self) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
+ --> $DIR/len_without_is_empty.rs:96:5
+ |
+LL | pub fn is_empty(&self) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(self) -> bool`
+
+error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method
+ --> $DIR/len_without_is_empty.rs:171:1
+ |
+LL | / pub trait DependsOnFoo: Foo {
+LL | | fn len(&mut self) -> usize;
+LL | | }
+ | |_^
+
+error: struct `OptionalLen3` has a public `len` method, but the `is_empty` method has an unexpected signature
+ --> $DIR/len_without_is_empty.rs:216:5
+ |
+LL | pub fn len(&self) -> usize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
+ --> $DIR/len_without_is_empty.rs:221:5
+ |
+LL | pub fn is_empty(&self) -> Option<bool> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool`
+
+error: struct `ResultLen` has a public `len` method, but the `is_empty` method has an unexpected signature
+ --> $DIR/len_without_is_empty.rs:228:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: `is_empty` defined here
+ --> $DIR/len_without_is_empty.rs:233:5
+ |
+LL | pub fn is_empty(&self) -> Option<bool> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected signature: `(&self) -> bool` or `(&self) -> Result<bool>
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/len_without_is_empty.rs:228:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::result-unit-err` implied by `-D warnings`
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/len_without_is_empty.rs:240:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/len_without_is_empty.rs:244:5
+ |
+LL | pub fn is_empty(&self) -> Result<bool, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/len_without_is_empty.rs:251:5
+ |
+LL | pub fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed
new file mode 100644
index 000000000..1f3b8ac99
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero.fixed
@@ -0,0 +1,143 @@
+// run-rustfix
+
+#![warn(clippy::len_zero)]
+#![allow(dead_code, unused, clippy::len_without_is_empty)]
+
+pub struct One;
+struct Wither;
+
+trait TraitsToo {
+ fn len(&self) -> isize;
+ // No error; `len` is private; see issue #1085.
+}
+
+impl TraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+pub struct HasIsEmpty;
+
+impl HasIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub struct HasWrongIsEmpty;
+
+impl HasWrongIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self, x: u32) -> bool {
+ false
+ }
+}
+
+pub trait WithIsEmpty {
+ fn len(&self) -> isize;
+ fn is_empty(&self) -> bool;
+}
+
+impl WithIsEmpty for Wither {
+ fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+fn main() {
+ let x = [1, 2];
+ if x.is_empty() {
+ println!("This should not happen!");
+ }
+
+ if "".is_empty() {}
+
+ let y = One;
+ if y.len() == 0 {
+ // No error; `One` does not have `.is_empty()`.
+ println!("This should not happen either!");
+ }
+
+ let z: &dyn TraitsToo = &y;
+ if z.len() > 0 {
+ // No error; `TraitsToo` has no `.is_empty()` method.
+ println!("Nor should this!");
+ }
+
+ let has_is_empty = HasIsEmpty;
+ if has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if has_is_empty.len() > 1 {
+ // No error.
+ println!("This can happen.");
+ }
+ if has_is_empty.len() <= 1 {
+ // No error.
+ println!("This can happen.");
+ }
+ if has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if !has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if has_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ if 1 < has_is_empty.len() {
+ // No error.
+ println!("This can happen.");
+ }
+ if 1 >= has_is_empty.len() {
+ // No error.
+ println!("This can happen.");
+ }
+ assert!(!has_is_empty.is_empty());
+
+ let with_is_empty: &dyn WithIsEmpty = &Wither;
+ if with_is_empty.is_empty() {
+ println!("Or this!");
+ }
+ assert!(!with_is_empty.is_empty());
+
+ let has_wrong_is_empty = HasWrongIsEmpty;
+ if has_wrong_is_empty.len() == 0 {
+ // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
+ println!("Or this!");
+ }
+}
+
+fn test_slice(b: &[u8]) {
+ if !b.is_empty() {}
+}
diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs
new file mode 100644
index 000000000..dc21de000
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero.rs
@@ -0,0 +1,143 @@
+// run-rustfix
+
+#![warn(clippy::len_zero)]
+#![allow(dead_code, unused, clippy::len_without_is_empty)]
+
+pub struct One;
+struct Wither;
+
+trait TraitsToo {
+ fn len(&self) -> isize;
+ // No error; `len` is private; see issue #1085.
+}
+
+impl TraitsToo for One {
+ fn len(&self) -> isize {
+ 0
+ }
+}
+
+pub struct HasIsEmpty;
+
+impl HasIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+pub struct HasWrongIsEmpty;
+
+impl HasWrongIsEmpty {
+ pub fn len(&self) -> isize {
+ 1
+ }
+
+ pub fn is_empty(&self, x: u32) -> bool {
+ false
+ }
+}
+
+pub trait WithIsEmpty {
+ fn len(&self) -> isize;
+ fn is_empty(&self) -> bool;
+}
+
+impl WithIsEmpty for Wither {
+ fn len(&self) -> isize {
+ 1
+ }
+
+ fn is_empty(&self) -> bool {
+ false
+ }
+}
+
+fn main() {
+ let x = [1, 2];
+ if x.len() == 0 {
+ println!("This should not happen!");
+ }
+
+ if "".len() == 0 {}
+
+ let y = One;
+ if y.len() == 0 {
+ // No error; `One` does not have `.is_empty()`.
+ println!("This should not happen either!");
+ }
+
+ let z: &dyn TraitsToo = &y;
+ if z.len() > 0 {
+ // No error; `TraitsToo` has no `.is_empty()` method.
+ println!("Nor should this!");
+ }
+
+ let has_is_empty = HasIsEmpty;
+ if has_is_empty.len() == 0 {
+ println!("Or this!");
+ }
+ if has_is_empty.len() != 0 {
+ println!("Or this!");
+ }
+ if has_is_empty.len() > 0 {
+ println!("Or this!");
+ }
+ if has_is_empty.len() < 1 {
+ println!("Or this!");
+ }
+ if has_is_empty.len() >= 1 {
+ println!("Or this!");
+ }
+ if has_is_empty.len() > 1 {
+ // No error.
+ println!("This can happen.");
+ }
+ if has_is_empty.len() <= 1 {
+ // No error.
+ println!("This can happen.");
+ }
+ if 0 == has_is_empty.len() {
+ println!("Or this!");
+ }
+ if 0 != has_is_empty.len() {
+ println!("Or this!");
+ }
+ if 0 < has_is_empty.len() {
+ println!("Or this!");
+ }
+ if 1 <= has_is_empty.len() {
+ println!("Or this!");
+ }
+ if 1 > has_is_empty.len() {
+ println!("Or this!");
+ }
+ if 1 < has_is_empty.len() {
+ // No error.
+ println!("This can happen.");
+ }
+ if 1 >= has_is_empty.len() {
+ // No error.
+ println!("This can happen.");
+ }
+ assert!(!has_is_empty.is_empty());
+
+ let with_is_empty: &dyn WithIsEmpty = &Wither;
+ if with_is_empty.len() == 0 {
+ println!("Or this!");
+ }
+ assert!(!with_is_empty.is_empty());
+
+ let has_wrong_is_empty = HasWrongIsEmpty;
+ if has_wrong_is_empty.len() == 0 {
+ // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
+ println!("Or this!");
+ }
+}
+
+fn test_slice(b: &[u8]) {
+ if b.len() != 0 {}
+}
diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr
new file mode 100644
index 000000000..6c71f1bee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero.stderr
@@ -0,0 +1,88 @@
+error: length comparison to zero
+ --> $DIR/len_zero.rs:61:8
+ |
+LL | if x.len() == 0 {
+ | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()`
+ |
+ = note: `-D clippy::len-zero` implied by `-D warnings`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:65:8
+ |
+LL | if "".len() == 0 {}
+ | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:80:8
+ |
+LL | if has_is_empty.len() == 0 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:83:8
+ |
+LL | if has_is_empty.len() != 0 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:86:8
+ |
+LL | if has_is_empty.len() > 0 {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to one
+ --> $DIR/len_zero.rs:89:8
+ |
+LL | if has_is_empty.len() < 1 {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
+
+error: length comparison to one
+ --> $DIR/len_zero.rs:92:8
+ |
+LL | if has_is_empty.len() >= 1 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:103:8
+ |
+LL | if 0 == has_is_empty.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:106:8
+ |
+LL | if 0 != has_is_empty.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:109:8
+ |
+LL | if 0 < has_is_empty.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to one
+ --> $DIR/len_zero.rs:112:8
+ |
+LL | if 1 <= has_is_empty.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
+
+error: length comparison to one
+ --> $DIR/len_zero.rs:115:8
+ |
+LL | if 1 > has_is_empty.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:129:8
+ |
+LL | if with_is_empty.len() == 0 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()`
+
+error: length comparison to zero
+ --> $DIR/len_zero.rs:142:8
+ |
+LL | if b.len() != 0 {}
+ | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.fixed b/src/tools/clippy/tests/ui/len_zero_ranges.fixed
new file mode 100644
index 000000000..797817662
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero_ranges.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![warn(clippy::len_zero)]
+#![allow(unused)]
+
+// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
+mod issue_3807 {
+ fn suggestion_is_fine_range() {
+ let _ = (0..42).is_empty();
+ }
+
+ fn suggestion_is_fine_range_inclusive() {
+ let _ = (0_u8..=42).is_empty();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.rs b/src/tools/clippy/tests/ui/len_zero_ranges.rs
new file mode 100644
index 000000000..a0eb51cc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero_ranges.rs
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![warn(clippy::len_zero)]
+#![allow(unused)]
+
+// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
+mod issue_3807 {
+ fn suggestion_is_fine_range() {
+ let _ = (0..42).len() == 0;
+ }
+
+ fn suggestion_is_fine_range_inclusive() {
+ let _ = (0_u8..=42).len() == 0;
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.stderr b/src/tools/clippy/tests/ui/len_zero_ranges.stderr
new file mode 100644
index 000000000..d0defb5a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/len_zero_ranges.stderr
@@ -0,0 +1,16 @@
+error: length comparison to zero
+ --> $DIR/len_zero_ranges.rs:9:17
+ |
+LL | let _ = (0..42).len() == 0;
+ | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()`
+ |
+ = note: `-D clippy::len-zero` implied by `-D warnings`
+
+error: length comparison to zero
+ --> $DIR/len_zero_ranges.rs:13:17
+ |
+LL | let _ = (0_u8..=42).len() == 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs
new file mode 100644
index 000000000..bb162adc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_and_return.rs
@@ -0,0 +1,169 @@
+#![allow(unused)]
+#![warn(clippy::let_and_return)]
+
+fn test() -> i32 {
+ let _y = 0; // no warning
+ let x = 5;
+ x
+}
+
+fn test_inner() -> i32 {
+ if true {
+ let x = 5;
+ x
+ } else {
+ 0
+ }
+}
+
+fn test_nowarn_1() -> i32 {
+ let mut x = 5;
+ x += 1;
+ x
+}
+
+fn test_nowarn_2() -> i32 {
+ let x = 5;
+ x + 1
+}
+
+fn test_nowarn_3() -> (i32, i32) {
+ // this should technically warn, but we do not compare complex patterns
+ let (x, y) = (5, 9);
+ (x, y)
+}
+
+fn test_nowarn_4() -> i32 {
+ // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type
+ let x: i32 = 5;
+ x
+}
+
+fn test_nowarn_5(x: i16) -> u16 {
+ #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
+ let x = x as u16;
+ x
+}
+
+// False positive example
+trait Decode {
+ fn decode<D: std::io::Read>(d: D) -> Result<Self, ()>
+ where
+ Self: Sized;
+}
+
+macro_rules! tuple_encode {
+ ($($x:ident),*) => (
+ impl<$($x: Decode),*> Decode for ($($x),*) {
+ #[inline]
+ #[allow(non_snake_case)]
+ fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> {
+ // Shouldn't trigger lint
+ Ok(($({let $x = Decode::decode(&mut d)?; $x }),*))
+ }
+ }
+ );
+}
+
+tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
+
+mod no_lint_if_stmt_borrows {
+ mod issue_3792 {
+ use std::io::{self, BufRead, Stdin};
+
+ fn read_line() -> String {
+ let stdin = io::stdin();
+ let line = stdin.lock().lines().next().unwrap().unwrap();
+ line
+ }
+ }
+
+ mod issue_3324 {
+ use std::cell::RefCell;
+ use std::rc::{Rc, Weak};
+
+ fn test(value: Weak<RefCell<Bar>>) -> u32 {
+ let value = value.upgrade().unwrap();
+ let ret = value.borrow().baz();
+ ret
+ }
+
+ struct Bar;
+
+ impl Bar {
+ fn new() -> Self {
+ Bar {}
+ }
+ fn baz(&self) -> u32 {
+ 0
+ }
+ }
+
+ fn main() {
+ let a = Rc::new(RefCell::new(Bar::new()));
+ let b = Rc::downgrade(&a);
+ test(b);
+ }
+ }
+
+ mod free_function {
+ struct Inner;
+
+ struct Foo<'a> {
+ inner: &'a Inner,
+ }
+
+ impl Drop for Foo<'_> {
+ fn drop(&mut self) {}
+ }
+
+ impl<'a> Foo<'a> {
+ fn new(inner: &'a Inner) -> Self {
+ Self { inner }
+ }
+
+ fn value(&self) -> i32 {
+ 42
+ }
+ }
+
+ fn some_foo(inner: &Inner) -> Foo<'_> {
+ Foo { inner }
+ }
+
+ fn test() -> i32 {
+ let x = Inner {};
+ let value = some_foo(&x).value();
+ value
+ }
+
+ fn test2() -> i32 {
+ let x = Inner {};
+ let value = Foo::new(&x).value();
+ value
+ }
+ }
+}
+
+mod issue_5729 {
+ use std::sync::Arc;
+
+ trait Foo {}
+
+ trait FooStorage {
+ fn foo_cloned(&self) -> Arc<dyn Foo>;
+ }
+
+ struct FooStorageImpl<T: Foo> {
+ foo: Arc<T>,
+ }
+
+ impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
+ fn foo_cloned(&self) -> Arc<dyn Foo> {
+ let clone = Arc::clone(&self.foo);
+ clone
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr
new file mode 100644
index 000000000..17fd694bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_and_return.stderr
@@ -0,0 +1,45 @@
+error: returning the result of a `let` binding from a block
+ --> $DIR/let_and_return.rs:7:5
+ |
+LL | let x = 5;
+ | ---------- unnecessary `let` binding
+LL | x
+ | ^
+ |
+ = note: `-D clippy::let-and-return` implied by `-D warnings`
+help: return the expression directly
+ |
+LL ~
+LL ~ 5
+ |
+
+error: returning the result of a `let` binding from a block
+ --> $DIR/let_and_return.rs:13:9
+ |
+LL | let x = 5;
+ | ---------- unnecessary `let` binding
+LL | x
+ | ^
+ |
+help: return the expression directly
+ |
+LL ~
+LL ~ 5
+ |
+
+error: returning the result of a `let` binding from a block
+ --> $DIR/let_and_return.rs:164:13
+ |
+LL | let clone = Arc::clone(&self.foo);
+ | ---------------------------------- unnecessary `let` binding
+LL | clone
+ | ^^^^^
+ |
+help: return the expression directly
+ |
+LL ~
+LL ~ Arc::clone(&self.foo) as _
+ |
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs
new file mode 100644
index 000000000..c5cb2eb1f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_if_seq.rs
@@ -0,0 +1,122 @@
+#![allow(
+ unused_variables,
+ unused_assignments,
+ clippy::similar_names,
+ clippy::blacklisted_name,
+ clippy::branches_sharing_code,
+ clippy::needless_late_init
+)]
+#![warn(clippy::useless_let_if_seq)]
+
+fn f() -> bool {
+ true
+}
+fn g(x: i32) -> i32 {
+ x + 1
+}
+
+fn issue985() -> i32 {
+ let mut x = 42;
+ if f() {
+ x = g(x);
+ }
+
+ x
+}
+
+fn issue985_alt() -> i32 {
+ let mut x = 42;
+ if f() {
+ f();
+ } else {
+ x = g(x);
+ }
+
+ x
+}
+
+#[allow(clippy::manual_strip)]
+fn issue975() -> String {
+ let mut udn = "dummy".to_string();
+ if udn.starts_with("uuid:") {
+ udn = String::from(&udn[5..]);
+ }
+ udn
+}
+
+fn early_return() -> u8 {
+ // FIXME: we could extend the lint to include such cases:
+ let foo;
+
+ if f() {
+ return 42;
+ } else {
+ foo = 0;
+ }
+
+ foo
+}
+
+fn main() {
+ early_return();
+ issue975();
+ issue985();
+ issue985_alt();
+
+ let mut foo = 0;
+ if f() {
+ foo = 42;
+ }
+
+ let mut bar = 0;
+ if f() {
+ f();
+ bar = 42;
+ } else {
+ f();
+ }
+
+ let quz;
+ if f() {
+ quz = 42;
+ } else {
+ quz = 0;
+ }
+
+ // `toto` is used several times
+ let mut toto;
+ if f() {
+ toto = 42;
+ } else {
+ for i in &[1, 2] {
+ toto = *i;
+ }
+
+ toto = 2;
+ }
+
+ // found in libcore, the inner if is not a statement but the block's expr
+ let mut ch = b'x';
+ if f() {
+ ch = b'*';
+ if f() {
+ ch = b'?';
+ }
+ }
+
+ // baz needs to be mut
+ let mut baz = 0;
+ if f() {
+ baz = 42;
+ }
+
+ baz = 1337;
+
+ // issue 3043 - types with interior mutability should not trigger this lint
+ use std::cell::Cell;
+ let mut val = Cell::new(1);
+ if true {
+ val = Cell::new(2);
+ }
+ println!("{}", val.get());
+}
diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr
new file mode 100644
index 000000000..271ccce68
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_if_seq.stderr
@@ -0,0 +1,50 @@
+error: `if _ { .. } else { .. }` is an expression
+ --> $DIR/let_if_seq.rs:66:5
+ |
+LL | / let mut foo = 0;
+LL | | if f() {
+LL | | foo = 42;
+LL | | }
+ | |_____^ help: it is more idiomatic to write: `let <mut> foo = if f() { 42 } else { 0 };`
+ |
+ = note: `-D clippy::useless-let-if-seq` implied by `-D warnings`
+ = note: you might not need `mut` at all
+
+error: `if _ { .. } else { .. }` is an expression
+ --> $DIR/let_if_seq.rs:71:5
+ |
+LL | / let mut bar = 0;
+LL | | if f() {
+LL | | f();
+LL | | bar = 42;
+LL | | } else {
+LL | | f();
+LL | | }
+ | |_____^ help: it is more idiomatic to write: `let <mut> bar = if f() { ..; 42 } else { ..; 0 };`
+ |
+ = note: you might not need `mut` at all
+
+error: `if _ { .. } else { .. }` is an expression
+ --> $DIR/let_if_seq.rs:79:5
+ |
+LL | / let quz;
+LL | | if f() {
+LL | | quz = 42;
+LL | | } else {
+LL | | quz = 0;
+LL | | }
+ | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
+
+error: `if _ { .. } else { .. }` is an expression
+ --> $DIR/let_if_seq.rs:108:5
+ |
+LL | / let mut baz = 0;
+LL | | if f() {
+LL | | baz = 42;
+LL | | }
+ | |_____^ help: it is more idiomatic to write: `let <mut> baz = if f() { 42 } else { 0 };`
+ |
+ = note: you might not need `mut` at all
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_underscore_drop.rs b/src/tools/clippy/tests/ui/let_underscore_drop.rs
new file mode 100644
index 000000000..11b50492a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_drop.rs
@@ -0,0 +1,28 @@
+#![warn(clippy::let_underscore_drop)]
+#![allow(clippy::let_unit_value)]
+
+struct Droppable;
+
+impl Drop for Droppable {
+ fn drop(&mut self) {}
+}
+
+fn main() {
+ let unit = ();
+ let boxed = Box::new(());
+ let droppable = Droppable;
+ let optional = Some(Droppable);
+
+ let _ = ();
+ let _ = Box::new(());
+ let _ = Droppable;
+ let _ = Some(Droppable);
+
+ // no lint for reference
+ let _ = droppable_ref();
+}
+
+#[must_use]
+fn droppable_ref() -> &'static mut Droppable {
+ unimplemented!()
+}
diff --git a/src/tools/clippy/tests/ui/let_underscore_drop.stderr b/src/tools/clippy/tests/ui/let_underscore_drop.stderr
new file mode 100644
index 000000000..ee7bbe995
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_drop.stderr
@@ -0,0 +1,27 @@
+error: non-binding `let` on a type that implements `Drop`
+ --> $DIR/let_underscore_drop.rs:17:5
+ |
+LL | let _ = Box::new(());
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::let-underscore-drop` implied by `-D warnings`
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding `let` on a type that implements `Drop`
+ --> $DIR/let_underscore_drop.rs:18:5
+ |
+LL | let _ = Droppable;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding `let` on a type that implements `Drop`
+ --> $DIR/let_underscore_drop.rs:19:5
+ |
+LL | let _ = Some(Droppable);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.rs b/src/tools/clippy/tests/ui/let_underscore_lock.rs
new file mode 100644
index 000000000..7a7c4e924
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.rs
@@ -0,0 +1,36 @@
+#![warn(clippy::let_underscore_lock)]
+
+extern crate parking_lot;
+
+fn main() {
+ let m = std::sync::Mutex::new(());
+ let rw = std::sync::RwLock::new(());
+
+ let _ = m.lock();
+ let _ = rw.read();
+ let _ = rw.write();
+ let _ = m.try_lock();
+ let _ = rw.try_read();
+ let _ = rw.try_write();
+
+ // These shouldn't throw an error.
+ let _ = m;
+ let _ = rw;
+
+ use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
+
+ let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
+ let _ = p_m.lock();
+
+ let p_m1 = Mutex::new(0);
+ let _ = p_m1.lock();
+
+ let p_rw = RwLock::new(0);
+ let _ = p_rw.read();
+ let _ = p_rw.write();
+
+ // These shouldn't throw an error.
+ let _ = p_m;
+ let _ = p_m1;
+ let _ = p_rw;
+}
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.stderr b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
new file mode 100644
index 000000000..4365b48fa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
@@ -0,0 +1,83 @@
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:9:5
+ |
+LL | let _ = m.lock();
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:10:5
+ |
+LL | let _ = rw.read();
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:11:5
+ |
+LL | let _ = rw.write();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:12:5
+ |
+LL | let _ = m.try_lock();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:13:5
+ |
+LL | let _ = rw.try_read();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:14:5
+ |
+LL | let _ = rw.try_write();
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:23:5
+ |
+LL | let _ = p_m.lock();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:26:5
+ |
+LL | let _ = p_m1.lock();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:29:5
+ |
+LL | let _ = p_rw.read();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:30:5
+ |
+LL | let _ = p_rw.write();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.rs b/src/tools/clippy/tests/ui/let_underscore_must_use.rs
new file mode 100644
index 000000000..1edb77c74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_must_use.rs
@@ -0,0 +1,95 @@
+#![warn(clippy::let_underscore_must_use)]
+#![allow(clippy::unnecessary_wraps)]
+
+// Debug implementations can fire this lint,
+// so we shouldn't lint external macros
+#[derive(Debug)]
+struct Foo {
+ field: i32,
+}
+
+#[must_use]
+fn f() -> u32 {
+ 0
+}
+
+fn g() -> Result<u32, u32> {
+ Ok(0)
+}
+
+#[must_use]
+fn l<T>(x: T) -> T {
+ x
+}
+
+fn h() -> u32 {
+ 0
+}
+
+struct S;
+
+impl S {
+ #[must_use]
+ pub fn f(&self) -> u32 {
+ 0
+ }
+
+ pub fn g(&self) -> Result<u32, u32> {
+ Ok(0)
+ }
+
+ fn k(&self) -> u32 {
+ 0
+ }
+
+ #[must_use]
+ fn h() -> u32 {
+ 0
+ }
+
+ fn p() -> Result<u32, u32> {
+ Ok(0)
+ }
+}
+
+trait Trait {
+ #[must_use]
+ fn a() -> u32;
+}
+
+impl Trait for S {
+ fn a() -> u32 {
+ 0
+ }
+}
+
+fn main() {
+ let _ = f();
+ let _ = g();
+ let _ = h();
+ let _ = l(0_u32);
+
+ let s = S {};
+
+ let _ = s.f();
+ let _ = s.g();
+ let _ = s.k();
+
+ let _ = S::h();
+ let _ = S::p();
+
+ let _ = S::a();
+
+ let _ = if true { Ok(()) } else { Err(()) };
+
+ let a = Result::<(), ()>::Ok(());
+
+ let _ = a.is_ok();
+
+ let _ = a.map(|_| ());
+
+ let _ = a;
+
+ #[allow(clippy::let_underscore_must_use)]
+ let _ = a;
+}
diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.stderr b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr
new file mode 100644
index 000000000..5b751ea56
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr
@@ -0,0 +1,99 @@
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:67:5
+ |
+LL | let _ = f();
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::let-underscore-must-use` implied by `-D warnings`
+ = help: consider explicitly using function result
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:68:5
+ |
+LL | let _ = g();
+ | ^^^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:70:5
+ |
+LL | let _ = l(0_u32);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using function result
+
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:74:5
+ |
+LL | let _ = s.f();
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using function result
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:75:5
+ |
+LL | let _ = s.g();
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:78:5
+ |
+LL | let _ = S::h();
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using function result
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:79:5
+ |
+LL | let _ = S::p();
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:81:5
+ |
+LL | let _ = S::a();
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using function result
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:83:5
+ |
+LL | let _ = if true { Ok(()) } else { Err(()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: non-binding let on a result of a `#[must_use]` function
+ --> $DIR/let_underscore_must_use.rs:87:5
+ |
+LL | let _ = a.is_ok();
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using function result
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:89:5
+ |
+LL | let _ = a.map(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: non-binding let on an expression with `#[must_use]` type
+ --> $DIR/let_underscore_must_use.rs:91:5
+ |
+LL | let _ = a;
+ | ^^^^^^^^^^
+ |
+ = help: consider explicitly using expression value
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed
new file mode 100644
index 000000000..6343cff0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_unit.fixed
@@ -0,0 +1,177 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![warn(clippy::let_unit_value)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
+
+macro_rules! let_and_return {
+ ($n:expr) => {{
+ let ret = $n;
+ }};
+}
+
+fn main() {
+ println!("x");
+ let _y = 1; // this is fine
+ let _z = ((), 1); // this as well
+ if true {
+ ();
+ }
+
+ consume_units_with_for_loop(); // should be fine as well
+
+ multiline_sugg();
+
+ let_and_return!(()) // should be fine
+}
+
+// Related to issue #1964
+fn consume_units_with_for_loop() {
+ // `for_let_unit` lint should not be triggered by consuming them using for loop.
+ let v = vec![(), (), ()];
+ let mut count = 0;
+ for _ in v {
+ count += 1;
+ }
+ assert_eq!(count, 3);
+
+ // Same for consuming from some other Iterator<Item = ()>.
+ let (tx, rx) = ::std::sync::mpsc::channel();
+ tx.send(()).unwrap();
+ drop(tx);
+
+ count = 0;
+ for _ in rx.iter() {
+ count += 1;
+ }
+ assert_eq!(count, 1);
+}
+
+fn multiline_sugg() {
+ let v: Vec<u8> = vec![2];
+
+ v
+ .into_iter()
+ .map(|i| i * 2)
+ .filter(|i| i % 2 == 0)
+ .map(|_| ())
+ .next()
+ .unwrap();
+}
+
+#[derive(Copy, Clone)]
+pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+ fn f<T>() -> T {
+ unimplemented!()
+ }
+ fn f2<T, U>(_: T) -> U {
+ unimplemented!()
+ }
+ fn f3<T>(x: T) -> T {
+ x
+ }
+ fn f5<T: Default>(x: bool) -> Option<T> {
+ x.then(|| T::default())
+ }
+
+ let _: () = f(); // Ok
+ let _: () = f(); // Lint.
+
+ let _: () = f2(0i32); // Ok
+ let _: () = f2(0i32); // Lint.
+
+ f3(()); // Lint
+ f3(()); // Lint
+
+ // Should lint:
+ // fn f4<T>(mut x: Vec<T>) -> T {
+ // x.pop().unwrap()
+ // }
+ // let _: () = f4(vec![()]);
+ // let x: () = f4(vec![()]);
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let _: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
+
+ let _: () = f5(true).unwrap();
+
+ #[allow(clippy::let_unit_value)]
+ {
+ let x = f();
+ let y;
+ let z;
+ match 0 {
+ 0 => {
+ y = f();
+ z = f();
+ },
+ 1 => {
+ println!("test");
+ y = f();
+ z = f3(());
+ },
+ _ => panic!(),
+ }
+
+ let x1;
+ let x2;
+ if true {
+ x1 = f();
+ x2 = x1;
+ } else {
+ x2 = f();
+ x1 = x2;
+ }
+
+ let opt;
+ match f5(true) {
+ Some(x) => opt = x,
+ None => panic!(),
+ };
+
+ #[warn(clippy::let_unit_value)]
+ {
+ let _: () = x;
+ let _: () = y;
+ z;
+ let _: () = x1;
+ let _: () = x2;
+ let _: () = opt;
+ }
+ }
+
+ let () = f();
+}
+
+fn attributes() {
+ fn f() {}
+
+ #[allow(clippy::let_unit_value)]
+ let _ = f();
+ #[expect(clippy::let_unit_value)]
+ let _ = f();
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs
new file mode 100644
index 000000000..c9bb2849f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_unit.rs
@@ -0,0 +1,177 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![warn(clippy::let_unit_value)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
+
+macro_rules! let_and_return {
+ ($n:expr) => {{
+ let ret = $n;
+ }};
+}
+
+fn main() {
+ let _x = println!("x");
+ let _y = 1; // this is fine
+ let _z = ((), 1); // this as well
+ if true {
+ let _a = ();
+ }
+
+ consume_units_with_for_loop(); // should be fine as well
+
+ multiline_sugg();
+
+ let_and_return!(()) // should be fine
+}
+
+// Related to issue #1964
+fn consume_units_with_for_loop() {
+ // `for_let_unit` lint should not be triggered by consuming them using for loop.
+ let v = vec![(), (), ()];
+ let mut count = 0;
+ for _ in v {
+ count += 1;
+ }
+ assert_eq!(count, 3);
+
+ // Same for consuming from some other Iterator<Item = ()>.
+ let (tx, rx) = ::std::sync::mpsc::channel();
+ tx.send(()).unwrap();
+ drop(tx);
+
+ count = 0;
+ for _ in rx.iter() {
+ count += 1;
+ }
+ assert_eq!(count, 1);
+}
+
+fn multiline_sugg() {
+ let v: Vec<u8> = vec![2];
+
+ let _ = v
+ .into_iter()
+ .map(|i| i * 2)
+ .filter(|i| i % 2 == 0)
+ .map(|_| ())
+ .next()
+ .unwrap();
+}
+
+#[derive(Copy, Clone)]
+pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+ fn f<T>() -> T {
+ unimplemented!()
+ }
+ fn f2<T, U>(_: T) -> U {
+ unimplemented!()
+ }
+ fn f3<T>(x: T) -> T {
+ x
+ }
+ fn f5<T: Default>(x: bool) -> Option<T> {
+ x.then(|| T::default())
+ }
+
+ let _: () = f(); // Ok
+ let x: () = f(); // Lint.
+
+ let _: () = f2(0i32); // Ok
+ let x: () = f2(0i32); // Lint.
+
+ let _: () = f3(()); // Lint
+ let x: () = f3(()); // Lint
+
+ // Should lint:
+ // fn f4<T>(mut x: Vec<T>) -> T {
+ // x.pop().unwrap()
+ // }
+ // let _: () = f4(vec![()]);
+ // let x: () = f4(vec![()]);
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let x: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
+
+ let _: () = f5(true).unwrap();
+
+ #[allow(clippy::let_unit_value)]
+ {
+ let x = f();
+ let y;
+ let z;
+ match 0 {
+ 0 => {
+ y = f();
+ z = f();
+ },
+ 1 => {
+ println!("test");
+ y = f();
+ z = f3(());
+ },
+ _ => panic!(),
+ }
+
+ let x1;
+ let x2;
+ if true {
+ x1 = f();
+ x2 = x1;
+ } else {
+ x2 = f();
+ x1 = x2;
+ }
+
+ let opt;
+ match f5(true) {
+ Some(x) => opt = x,
+ None => panic!(),
+ };
+
+ #[warn(clippy::let_unit_value)]
+ {
+ let _: () = x;
+ let _: () = y;
+ let _: () = z;
+ let _: () = x1;
+ let _: () = x2;
+ let _: () = opt;
+ }
+ }
+
+ let () = f();
+}
+
+fn attributes() {
+ fn f() {}
+
+ #[allow(clippy::let_unit_value)]
+ let _ = f();
+ #[expect(clippy::let_unit_value)]
+ let _ = f();
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr
new file mode 100644
index 000000000..49da74ca7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_unit.stderr
@@ -0,0 +1,102 @@
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:14:5
+ |
+LL | let _x = println!("x");
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");`
+ |
+ = note: `-D clippy::let-unit-value` implied by `-D warnings`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:18:9
+ |
+LL | let _a = ();
+ | ^^^^^^^^^^^^ help: omit the `let` binding: `();`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:53:5
+ |
+LL | / let _ = v
+LL | | .into_iter()
+LL | | .map(|i| i * 2)
+LL | | .filter(|i| i % 2 == 0)
+LL | | .map(|_| ())
+LL | | .next()
+LL | | .unwrap();
+ | |__________________^
+ |
+help: omit the `let` binding
+ |
+LL ~ v
+LL + .into_iter()
+LL + .map(|i| i * 2)
+LL + .filter(|i| i % 2 == 0)
+LL + .map(|_| ())
+LL + .next()
+LL + .unwrap();
+ |
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:80:5
+ |
+LL | let x: () = f(); // Lint.
+ | ^^^^-^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:83:5
+ |
+LL | let x: () = f2(0i32); // Lint.
+ | ^^^^-^^^^^^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:85:5
+ |
+LL | let _: () = f3(()); // Lint
+ | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:86:5
+ |
+LL | let x: () = f3(()); // Lint
+ | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:102:5
+ |
+LL | let x: () = if true { f() } else { f2(0) }; // Lint
+ | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:113:5
+ |
+LL | / let _: () = match Some(0) {
+LL | | None => f2(1),
+LL | | Some(0) => f(),
+LL | | Some(1) => f2(3),
+LL | | Some(_) => (),
+LL | | };
+ | |______^
+ |
+help: omit the `let` binding
+ |
+LL ~ match Some(0) {
+LL + None => f2(1),
+LL + Some(0) => f(),
+LL + Some(1) => f2(3),
+LL + Some(_) => (),
+LL + };
+ |
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:160:13
+ |
+LL | let _: () = z;
+ | ^^^^^^^^^^^^^^ help: omit the `let` binding: `z;`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/linkedlist.rs b/src/tools/clippy/tests/ui/linkedlist.rs
new file mode 100644
index 000000000..690ea810a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/linkedlist.rs
@@ -0,0 +1,48 @@
+#![feature(associated_type_defaults)]
+#![warn(clippy::linkedlist)]
+#![allow(unused, dead_code, clippy::needless_pass_by_value)]
+
+extern crate alloc;
+use alloc::collections::linked_list::LinkedList;
+
+const C: LinkedList<i32> = LinkedList::new();
+static S: LinkedList<i32> = LinkedList::new();
+
+trait Foo {
+ type Baz = LinkedList<u8>;
+ fn foo(_: LinkedList<u8>);
+ const BAR: Option<LinkedList<u8>>;
+}
+
+// Ok, we don’t want to warn for implementations; see issue #605.
+impl Foo for LinkedList<u8> {
+ fn foo(_: LinkedList<u8>) {}
+ const BAR: Option<LinkedList<u8>> = None;
+}
+
+pub struct Bar {
+ priv_linked_list_field: LinkedList<u8>,
+ pub pub_linked_list_field: LinkedList<u8>,
+}
+impl Bar {
+ fn foo(_: LinkedList<u8>) {}
+}
+
+// All of these test should be trigger the lint because they are not
+// part of the public api
+fn test(my_favorite_linked_list: LinkedList<u8>) {}
+fn test_ret() -> Option<LinkedList<u8>> {
+ None
+}
+fn test_local_not_linted() {
+ let _: LinkedList<u8>;
+}
+
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(the_most_awesome_linked_list: LinkedList<u8>) {}
+pub fn pub_test_ret() -> Option<LinkedList<u8>> {
+ None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/linkedlist.stderr b/src/tools/clippy/tests/ui/linkedlist.stderr
new file mode 100644
index 000000000..51327df13
--- /dev/null
+++ b/src/tools/clippy/tests/ui/linkedlist.stderr
@@ -0,0 +1,75 @@
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:8:10
+ |
+LL | const C: LinkedList<i32> = LinkedList::new();
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::linkedlist` implied by `-D warnings`
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:9:11
+ |
+LL | static S: LinkedList<i32> = LinkedList::new();
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:12:16
+ |
+LL | type Baz = LinkedList<u8>;
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:13:15
+ |
+LL | fn foo(_: LinkedList<u8>);
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:14:23
+ |
+LL | const BAR: Option<LinkedList<u8>>;
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:24:29
+ |
+LL | priv_linked_list_field: LinkedList<u8>,
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:28:15
+ |
+LL | fn foo(_: LinkedList<u8>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:33:34
+ |
+LL | fn test(my_favorite_linked_list: LinkedList<u8>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+ --> $DIR/linkedlist.rs:34:25
+ |
+LL | fn test_ret() -> Option<LinkedList<u8>> {
+ | ^^^^^^^^^^^^^^
+ |
+ = help: a `VecDeque` might work
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/literals.rs b/src/tools/clippy/tests/ui/literals.rs
new file mode 100644
index 000000000..0cadd5a3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/literals.rs
@@ -0,0 +1,42 @@
+// does not test any rustfixable lints
+
+#![warn(clippy::mixed_case_hex_literals)]
+#![warn(clippy::zero_prefixed_literal)]
+#![warn(clippy::unseparated_literal_suffix)]
+#![warn(clippy::separated_literal_suffix)]
+#![allow(dead_code, overflowing_literals)]
+
+fn main() {
+ let ok1 = 0xABCD;
+ let ok3 = 0xab_cd;
+ let ok4 = 0xab_cd_i32;
+ let ok5 = 0xAB_CD_u32;
+ let ok5 = 0xAB_CD_isize;
+ let fail1 = 0xabCD;
+ let fail2 = 0xabCD_u32;
+ let fail2 = 0xabCD_isize;
+ let fail_multi_zero = 000_123usize;
+
+ let ok9 = 0;
+ let ok10 = 0_i64;
+ let fail8 = 0123;
+
+ let ok11 = 0o123;
+ let ok12 = 0b10_1010;
+
+ let ok13 = 0xab_abcd;
+ let ok14 = 0xBAFE_BAFE;
+ let ok15 = 0xab_cabc_abca_bcab_cabc;
+ let ok16 = 0xFE_BAFE_ABAB_ABCD;
+ let ok17 = 0x123_4567_8901_usize;
+ let ok18 = 0xF;
+
+ let fail19 = 12_3456_21;
+ let fail22 = 3__4___23;
+ let fail23 = 3__16___23;
+
+ let fail24 = 0xAB_ABC_AB;
+ let fail25 = 0b01_100_101;
+ let ok26 = 0x6_A0_BF;
+ let ok27 = 0b1_0010_0101;
+}
diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr
new file mode 100644
index 000000000..365b24074
--- /dev/null
+++ b/src/tools/clippy/tests/ui/literals.stderr
@@ -0,0 +1,139 @@
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:12:15
+ |
+LL | let ok4 = 0xab_cd_i32;
+ | ^^^^^^^^^^^ help: remove the underscore: `0xab_cdi32`
+ |
+ = note: `-D clippy::separated-literal-suffix` implied by `-D warnings`
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:13:15
+ |
+LL | let ok5 = 0xAB_CD_u32;
+ | ^^^^^^^^^^^ help: remove the underscore: `0xAB_CDu32`
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:14:15
+ |
+LL | let ok5 = 0xAB_CD_isize;
+ | ^^^^^^^^^^^^^ help: remove the underscore: `0xAB_CDisize`
+
+error: inconsistent casing in hexadecimal literal
+ --> $DIR/literals.rs:15:17
+ |
+LL | let fail1 = 0xabCD;
+ | ^^^^^^
+ |
+ = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:16:17
+ |
+LL | let fail2 = 0xabCD_u32;
+ | ^^^^^^^^^^ help: remove the underscore: `0xabCDu32`
+
+error: inconsistent casing in hexadecimal literal
+ --> $DIR/literals.rs:16:17
+ |
+LL | let fail2 = 0xabCD_u32;
+ | ^^^^^^^^^^
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:17:17
+ |
+LL | let fail2 = 0xabCD_isize;
+ | ^^^^^^^^^^^^ help: remove the underscore: `0xabCDisize`
+
+error: inconsistent casing in hexadecimal literal
+ --> $DIR/literals.rs:17:17
+ |
+LL | let fail2 = 0xabCD_isize;
+ | ^^^^^^^^^^^^
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/literals.rs:18:27
+ |
+LL | let fail_multi_zero = 000_123usize;
+ | ^^^^^^^^^^^^ help: add an underscore: `000_123_usize`
+ |
+ = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
+
+error: this is a decimal constant
+ --> $DIR/literals.rs:18:27
+ |
+LL | let fail_multi_zero = 000_123usize;
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
+help: if you mean to use a decimal constant, remove the `0` to avoid confusion
+ |
+LL | let fail_multi_zero = 123usize;
+ | ~~~~~~~~
+help: if you mean to use an octal constant, use `0o`
+ |
+LL | let fail_multi_zero = 0o123usize;
+ | ~~~~~~~~~~
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:21:16
+ |
+LL | let ok10 = 0_i64;
+ | ^^^^^ help: remove the underscore: `0i64`
+
+error: this is a decimal constant
+ --> $DIR/literals.rs:22:17
+ |
+LL | let fail8 = 0123;
+ | ^^^^
+ |
+help: if you mean to use a decimal constant, remove the `0` to avoid confusion
+ |
+LL | let fail8 = 123;
+ | ~~~
+help: if you mean to use an octal constant, use `0o`
+ |
+LL | let fail8 = 0o123;
+ | ~~~~~
+
+error: integer type suffix should not be separated by an underscore
+ --> $DIR/literals.rs:31:16
+ |
+LL | let ok17 = 0x123_4567_8901_usize;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/literals.rs:34:18
+ |
+LL | let fail19 = 12_3456_21;
+ | ^^^^^^^^^^ help: consider: `12_345_621`
+ |
+ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/literals.rs:35:18
+ |
+LL | let fail22 = 3__4___23;
+ | ^^^^^^^^^ help: consider: `3_423`
+
+error: digits grouped inconsistently by underscores
+ --> $DIR/literals.rs:36:18
+ |
+LL | let fail23 = 3__16___23;
+ | ^^^^^^^^^^ help: consider: `31_623`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:38:18
+ |
+LL | let fail24 = 0xAB_ABC_AB;
+ | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:39:18
+ |
+LL | let fail25 = 0b01_100_101;
+ | ^^^^^^^^^^^^ help: consider: `0b0110_0101`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/logic_bug.rs b/src/tools/clippy/tests/ui/logic_bug.rs
new file mode 100644
index 000000000..dd6b1db5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/logic_bug.rs
@@ -0,0 +1,34 @@
+#![feature(lint_reasons)]
+#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::logic_bug)]
+
+fn main() {
+ let a: bool = unimplemented!();
+ let b: bool = unimplemented!();
+ let c: bool = unimplemented!();
+ let d: bool = unimplemented!();
+ let e: bool = unimplemented!();
+ let _ = a && b || a;
+ let _ = !(a && b);
+ let _ = false && a;
+ // don't lint on cfgs
+ let _ = cfg!(you_shall_not_not_pass) && a;
+ let _ = a || !b || !c || !d || !e;
+ let _ = !(a && b || c);
+}
+
+fn equality_stuff() {
+ let a: i32 = unimplemented!();
+ let b: i32 = unimplemented!();
+ let _ = a == b && a != b;
+ let _ = a < b && a >= b;
+ let _ = a > b && a <= b;
+ let _ = a > b && a == b;
+}
+
+fn check_expect() {
+ let a: i32 = unimplemented!();
+ let b: i32 = unimplemented!();
+ #[expect(clippy::logic_bug)]
+ let _ = a < b && a >= b;
+}
diff --git a/src/tools/clippy/tests/ui/logic_bug.stderr b/src/tools/clippy/tests/ui/logic_bug.stderr
new file mode 100644
index 000000000..4021fbf45
--- /dev/null
+++ b/src/tools/clippy/tests/ui/logic_bug.stderr
@@ -0,0 +1,63 @@
+error: this boolean expression contains a logic bug
+ --> $DIR/logic_bug.rs:11:13
+ |
+LL | let _ = a && b || a;
+ | ^^^^^^^^^^^ help: it would look like the following: `a`
+ |
+ = note: `-D clippy::logic-bug` implied by `-D warnings`
+help: this expression can be optimized out by applying boolean operations to the outer expression
+ --> $DIR/logic_bug.rs:11:18
+ |
+LL | let _ = a && b || a;
+ | ^
+
+error: this boolean expression contains a logic bug
+ --> $DIR/logic_bug.rs:13:13
+ |
+LL | let _ = false && a;
+ | ^^^^^^^^^^ help: it would look like the following: `false`
+ |
+help: this expression can be optimized out by applying boolean operations to the outer expression
+ --> $DIR/logic_bug.rs:13:22
+ |
+LL | let _ = false && a;
+ | ^
+
+error: this boolean expression contains a logic bug
+ --> $DIR/logic_bug.rs:23:13
+ |
+LL | let _ = a == b && a != b;
+ | ^^^^^^^^^^^^^^^^ help: it would look like the following: `false`
+ |
+help: this expression can be optimized out by applying boolean operations to the outer expression
+ --> $DIR/logic_bug.rs:23:13
+ |
+LL | let _ = a == b && a != b;
+ | ^^^^^^
+
+error: this boolean expression contains a logic bug
+ --> $DIR/logic_bug.rs:24:13
+ |
+LL | let _ = a < b && a >= b;
+ | ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
+ |
+help: this expression can be optimized out by applying boolean operations to the outer expression
+ --> $DIR/logic_bug.rs:24:13
+ |
+LL | let _ = a < b && a >= b;
+ | ^^^^^
+
+error: this boolean expression contains a logic bug
+ --> $DIR/logic_bug.rs:25:13
+ |
+LL | let _ = a > b && a <= b;
+ | ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
+ |
+help: this expression can be optimized out by applying boolean operations to the outer expression
+ --> $DIR/logic_bug.rs:25:13
+ |
+LL | let _ = a > b && a <= b;
+ | ^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.fixed b/src/tools/clippy/tests/ui/lossy_float_literal.fixed
new file mode 100644
index 000000000..24e372354
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+#![warn(clippy::lossy_float_literal)]
+
+fn main() {
+ // Lossy whole-number float literals
+ let _: f32 = 16_777_216.0;
+ let _: f32 = 16_777_220.0;
+ let _: f32 = 16_777_220.0;
+ let _: f32 = 16_777_220.0;
+ let _ = 16_777_220_f32;
+ let _: f32 = -16_777_220.0;
+ let _: f64 = 9_007_199_254_740_992.0;
+ let _: f64 = 9_007_199_254_740_992.0;
+ let _: f64 = 9_007_199_254_740_992.0;
+ let _ = 9_007_199_254_740_992_f64;
+ let _: f64 = -9_007_199_254_740_992.0;
+
+ // Lossless whole number float literals
+ let _: f32 = 16_777_216.0;
+ let _: f32 = 16_777_218.0;
+ let _: f32 = 16_777_220.0;
+ let _: f32 = -16_777_216.0;
+ let _: f32 = -16_777_220.0;
+ let _: f64 = 16_777_217.0;
+ let _: f64 = -16_777_217.0;
+ let _: f64 = 9_007_199_254_740_992.0;
+ let _: f64 = -9_007_199_254_740_992.0;
+
+ // Ignored whole number float literals
+ let _: f32 = 1e25;
+ let _: f32 = 1E25;
+ let _: f64 = 1e99;
+ let _: f64 = 1E99;
+ let _: f32 = 0.1;
+}
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.rs b/src/tools/clippy/tests/ui/lossy_float_literal.rs
new file mode 100644
index 000000000..3dcf98fa0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.rs
@@ -0,0 +1,35 @@
+// run-rustfix
+#![warn(clippy::lossy_float_literal)]
+
+fn main() {
+ // Lossy whole-number float literals
+ let _: f32 = 16_777_217.0;
+ let _: f32 = 16_777_219.0;
+ let _: f32 = 16_777_219.;
+ let _: f32 = 16_777_219.000;
+ let _ = 16_777_219f32;
+ let _: f32 = -16_777_219.0;
+ let _: f64 = 9_007_199_254_740_993.0;
+ let _: f64 = 9_007_199_254_740_993.;
+ let _: f64 = 9_007_199_254_740_993.00;
+ let _ = 9_007_199_254_740_993f64;
+ let _: f64 = -9_007_199_254_740_993.0;
+
+ // Lossless whole number float literals
+ let _: f32 = 16_777_216.0;
+ let _: f32 = 16_777_218.0;
+ let _: f32 = 16_777_220.0;
+ let _: f32 = -16_777_216.0;
+ let _: f32 = -16_777_220.0;
+ let _: f64 = 16_777_217.0;
+ let _: f64 = -16_777_217.0;
+ let _: f64 = 9_007_199_254_740_992.0;
+ let _: f64 = -9_007_199_254_740_992.0;
+
+ // Ignored whole number float literals
+ let _: f32 = 1e25;
+ let _: f32 = 1E25;
+ let _: f64 = 1e99;
+ let _: f64 = 1E99;
+ let _: f32 = 0.1;
+}
diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
new file mode 100644
index 000000000..d2193c0c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr
@@ -0,0 +1,70 @@
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:6:18
+ |
+LL | let _: f32 = 16_777_217.0;
+ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
+ |
+ = note: `-D clippy::lossy-float-literal` implied by `-D warnings`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:7:18
+ |
+LL | let _: f32 = 16_777_219.0;
+ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:8:18
+ |
+LL | let _: f32 = 16_777_219.;
+ | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:9:18
+ |
+LL | let _: f32 = 16_777_219.000;
+ | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:10:13
+ |
+LL | let _ = 16_777_219f32;
+ | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:11:19
+ |
+LL | let _: f32 = -16_777_219.0;
+ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:12:18
+ |
+LL | let _: f64 = 9_007_199_254_740_993.0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:13:18
+ |
+LL | let _: f64 = 9_007_199_254_740_993.;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:14:18
+ |
+LL | let _: f64 = 9_007_199_254_740_993.00;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:15:13
+ |
+LL | let _ = 9_007_199_254_740_993f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64`
+
+error: literal cannot be represented as the underlying type without loss of precision
+ --> $DIR/lossy_float_literal.rs:16:19
+ |
+LL | let _: f64 = -9_007_199_254_740_993.0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
new file mode 100644
index 000000000..e612480d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -0,0 +1,48 @@
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// run-rustfix
+// ignore-32bit
+
+#![feature(lint_reasons)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+ use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};
+ use mac;
+ use mini_mac::ClippyMiniMacroTest;
+ use mini_mac;
+ use mac::{inner::foofoo, inner::try_err};
+ use mac::inner;
+ use mac::inner::nested::string_add;
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+// issue #7015, ICE due to calling `module_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
new file mode 100644
index 000000000..b34817cc3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -0,0 +1,48 @@
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// run-rustfix
+// ignore-32bit
+
+#![feature(lint_reasons)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+ #[macro_use]
+ use mac;
+ #[macro_use]
+ use mini_mac;
+ #[macro_use]
+ use mac::inner;
+ #[macro_use]
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+// issue #7015, ICE due to calling `module_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
new file mode 100644
index 000000000..bf7b6edd0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -0,0 +1,28 @@
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:23:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+ |
+ = note: `-D clippy::macro-use-imports` implied by `-D warnings`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:21:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:25:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+
+error: `macro_use` attributes are no longer needed in the Rust 2018 edition
+ --> $DIR/macro_use_imports.rs:19:5
+ |
+LL | #[macro_use]
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
new file mode 100644
index 000000000..8a1b05da9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
@@ -0,0 +1,51 @@
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// ignore-32bit
+
+#![feature(lint_reasons)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+ #[expect(clippy::macro_use_imports)]
+ #[macro_use]
+ use mac;
+ #[expect(clippy::macro_use_imports)]
+ #[macro_use]
+ use mini_mac;
+ #[expect(clippy::macro_use_imports)]
+ #[macro_use]
+ use mac::inner;
+ #[expect(clippy::macro_use_imports)]
+ #[macro_use]
+ use mac::inner::nested;
+
+ #[derive(ClippyMiniMacroTest)]
+ struct Test;
+
+ fn test() {
+ pub_macro!();
+ inner_mod_macro!();
+ pub_in_private_macro!(_var);
+ function_macro!();
+ let v: ty_macro!() = Vec::default();
+
+ inner::try_err!();
+ inner::foofoo!();
+ nested::string_add!();
+ }
+}
+
+// issue #7015, ICE due to calling `module_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
new file mode 100644
index 000000000..d0bc640db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
@@ -0,0 +1,52 @@
+// revisions: edition2018 edition2021
+// [edition2018] edition:2018
+// [edition2021] edition:2021
+// run-rustfix
+
+#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ assert!(a.is_empty(), "qaqaq{:?}", a);
+ assert!(a.is_empty(), "qwqwq");
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+ let b = vec![1, 2, 3];
+ assert!(!b.is_empty(), "panic1");
+ assert!(!(b.is_empty() && a.is_empty()), "panic2");
+ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
+ assert!(!(b.is_empty() || a.is_empty()), "panic4");
+ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+ assert!(!a.is_empty(), "with expansion {}", one!());
+}
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
new file mode 100644
index 000000000..a0f31afd6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
@@ -0,0 +1,68 @@
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:30:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qaqaq{:?}", a);
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
+ |
+ = note: `-D clippy::manual-assert` implied by `-D warnings`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:33:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qwqwq");
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:50:5
+ |
+LL | / if b.is_empty() {
+LL | | panic!("panic1");
+LL | | }
+ | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:53:5
+ |
+LL | / if b.is_empty() && a.is_empty() {
+LL | | panic!("panic2");
+LL | | }
+ | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:56:5
+ |
+LL | / if a.is_empty() && !b.is_empty() {
+LL | | panic!("panic3");
+LL | | }
+ | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:59:5
+ |
+LL | / if b.is_empty() || a.is_empty() {
+LL | | panic!("panic4");
+LL | | }
+ | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:62:5
+ |
+LL | / if a.is_empty() || !b.is_empty() {
+LL | | panic!("panic5");
+LL | | }
+ | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:65:5
+ |
+LL | / if a.is_empty() {
+LL | | panic!("with expansion {}", one!())
+LL | | }
+ | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
new file mode 100644
index 000000000..d0bc640db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
@@ -0,0 +1,52 @@
+// revisions: edition2018 edition2021
+// [edition2018] edition:2018
+// [edition2021] edition:2021
+// run-rustfix
+
+#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ assert!(a.is_empty(), "qaqaq{:?}", a);
+ assert!(a.is_empty(), "qwqwq");
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+ let b = vec![1, 2, 3];
+ assert!(!b.is_empty(), "panic1");
+ assert!(!(b.is_empty() && a.is_empty()), "panic2");
+ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
+ assert!(!(b.is_empty() || a.is_empty()), "panic4");
+ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+ assert!(!a.is_empty(), "with expansion {}", one!());
+}
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
new file mode 100644
index 000000000..a0f31afd6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
@@ -0,0 +1,68 @@
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:30:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qaqaq{:?}", a);
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
+ |
+ = note: `-D clippy::manual-assert` implied by `-D warnings`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:33:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qwqwq");
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:50:5
+ |
+LL | / if b.is_empty() {
+LL | | panic!("panic1");
+LL | | }
+ | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:53:5
+ |
+LL | / if b.is_empty() && a.is_empty() {
+LL | | panic!("panic2");
+LL | | }
+ | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:56:5
+ |
+LL | / if a.is_empty() && !b.is_empty() {
+LL | | panic!("panic3");
+LL | | }
+ | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:59:5
+ |
+LL | / if b.is_empty() || a.is_empty() {
+LL | | panic!("panic4");
+LL | | }
+ | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:62:5
+ |
+LL | / if a.is_empty() || !b.is_empty() {
+LL | | panic!("panic5");
+LL | | }
+ | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:65:5
+ |
+LL | / if a.is_empty() {
+LL | | panic!("with expansion {}", one!())
+LL | | }
+ | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_assert.fixed b/src/tools/clippy/tests/ui/manual_assert.fixed
new file mode 100644
index 000000000..6c2a25c37
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.fixed
@@ -0,0 +1,45 @@
+// revisions: edition2018 edition2021
+// [edition2018] edition:2018
+// [edition2021] edition:2021
+// run-rustfix
+
+#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ assert!(a.is_empty(), "qaqaq{:?}", a);
+ assert!(a.is_empty(), "qwqwq");
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+ let b = vec![1, 2, 3];
+ assert!(!b.is_empty(), "panic1");
+ assert!(!(b.is_empty() && a.is_empty()), "panic2");
+ assert!(!(a.is_empty() && !b.is_empty()), "panic3");
+ assert!(!(b.is_empty() || a.is_empty()), "panic4");
+ assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+}
diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs
new file mode 100644
index 000000000..027747d83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_assert.rs
@@ -0,0 +1,68 @@
+// revisions: edition2018 edition2021
+// [edition2018] edition:2018
+// [edition2021] edition:2021
+// run-rustfix
+
+#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
+
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qwqwq");
+ }
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+ let b = vec![1, 2, 3];
+ if b.is_empty() {
+ panic!("panic1");
+ }
+ if b.is_empty() && a.is_empty() {
+ panic!("panic2");
+ }
+ if a.is_empty() && !b.is_empty() {
+ panic!("panic3");
+ }
+ if b.is_empty() || a.is_empty() {
+ panic!("panic4");
+ }
+ if a.is_empty() || !b.is_empty() {
+ panic!("panic5");
+ }
+ if a.is_empty() {
+ panic!("with expansion {}", one!())
+ }
+}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed
new file mode 100644
index 000000000..b7e46a4a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed
@@ -0,0 +1,110 @@
+// run-rustfix
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+async fn fut() -> i32 { 42 }
+
+#[rustfmt::skip]
+async fn fut2() -> i32 { 42 }
+
+#[rustfmt::skip]
+async fn fut3() -> i32 { 42 }
+
+async fn empty_fut() {}
+
+#[rustfmt::skip]
+async fn empty_fut2() {}
+
+#[rustfmt::skip]
+async fn empty_fut3() {}
+
+async fn core_fut() -> i32 { 42 }
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S;
+impl S {
+ async fn inh_fut() -> i32 {
+ // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+ let a = 42;
+ let b = 21;
+ if a < b {
+ let c = 21;
+ let d = 42;
+ if c < d {
+ let _ = 42;
+ }
+ }
+ 42
+ }
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+// Tests related to lifetime capture
+
+async fn elided(_: &i32) -> i32 { 42 }
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+ use std::future::Future;
+
+ struct A;
+ impl A {
+ fn f(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+ }
+
+ fn test() {
+ let _future = {
+ let a = A;
+ a.f()
+ };
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs
new file mode 100644
index 000000000..b05429da6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_async_fn.rs
@@ -0,0 +1,130 @@
+// run-rustfix
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+fn fut() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+#[rustfmt::skip]
+fn fut2() ->impl Future<Output = i32> {
+ async { 42 }
+}
+
+#[rustfmt::skip]
+fn fut3()-> impl Future<Output = i32> {
+ async { 42 }
+}
+
+fn empty_fut() -> impl Future<Output = ()> {
+ async {}
+}
+
+#[rustfmt::skip]
+fn empty_fut2() ->impl Future<Output = ()> {
+ async {}
+}
+
+#[rustfmt::skip]
+fn empty_fut3()-> impl Future<Output = ()> {
+ async {}
+}
+
+fn core_fut() -> impl core::future::Future<Output = i32> {
+ async move { 42 }
+}
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S;
+impl S {
+ fn inh_fut() -> impl Future<Output = i32> {
+ async {
+ // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+ let a = 42;
+ let b = 21;
+ if a < b {
+ let c = 21;
+ let d = 42;
+ if c < d {
+ let _ = 42;
+ }
+ }
+ 42
+ }
+ }
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+// Tests related to lifetime capture
+
+fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+ async { 42 }
+}
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+ async { 42 }
+}
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+ use std::future::Future;
+
+ struct A;
+ impl A {
+ fn f(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+ }
+
+ fn test() {
+ let _future = {
+ let a = A;
+ a.f()
+ };
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr
new file mode 100644
index 000000000..0a903ed6f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr
@@ -0,0 +1,165 @@
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:7:1
+ |
+LL | fn fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-async-fn` implied by `-D warnings`
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut() -> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:12:1
+ |
+LL | fn fut2() ->impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut2() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut2() ->impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:17:1
+ |
+LL | fn fut3()-> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut3() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut3()-> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:21:1
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut() {
+ | ~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:26:1
+ |
+LL | fn empty_fut2() ->impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut2() {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut2() ->impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:31:1
+ |
+LL | fn empty_fut3()-> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut3() {
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut3()-> impl Future<Output = ()> {}
+ | ~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:35:1
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn core_fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:57:5
+ |
+LL | fn inh_fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn inh_fut() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL ~ fn inh_fut() -> impl Future<Output = i32> {
+LL + // NOTE: this code is here just to check that the indentation is correct in the suggested fix
+LL + let a = 42;
+LL + let b = 21;
+LL + if a < b {
+LL + let c = 21;
+LL + let d = 42;
+LL + if c < d {
+LL + let _ = 42;
+LL + }
+LL + }
+LL + 42
+LL + }
+ |
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:92:1
+ |
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn elided(_: &i32) -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:101:1
+ |
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
+ | ~~~~~~
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed
new file mode 100644
index 000000000..386360dbd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_bits.fixed
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![warn(clippy::manual_bits)]
+#![allow(
+ clippy::no_effect,
+ clippy::useless_conversion,
+ path_statements,
+ unused_must_use,
+ clippy::unnecessary_operation
+)]
+
+use std::mem::{size_of, size_of_val};
+
+fn main() {
+ i8::BITS as usize;
+ i16::BITS as usize;
+ i32::BITS as usize;
+ i64::BITS as usize;
+ i128::BITS as usize;
+ isize::BITS as usize;
+
+ u8::BITS as usize;
+ u16::BITS as usize;
+ u32::BITS as usize;
+ u64::BITS as usize;
+ u128::BITS as usize;
+ usize::BITS as usize;
+
+ i8::BITS as usize;
+ i16::BITS as usize;
+ i32::BITS as usize;
+ i64::BITS as usize;
+ i128::BITS as usize;
+ isize::BITS as usize;
+
+ u8::BITS as usize;
+ u16::BITS as usize;
+ u32::BITS as usize;
+ u64::BITS as usize;
+ u128::BITS as usize;
+ usize::BITS as usize;
+
+ size_of::<usize>() * 4;
+ 4 * size_of::<usize>();
+ size_of::<bool>() * 8;
+ 8 * size_of::<bool>();
+
+ size_of_val(&0u32) * 8;
+
+ type Word = u32;
+ Word::BITS as usize;
+ type Bool = bool;
+ size_of::<Bool>() * 8;
+
+ let _: u32 = u128::BITS as u32;
+ let _: u32 = u128::BITS.try_into().unwrap();
+ let _ = (u128::BITS as usize).pow(5);
+ let _ = &(u128::BITS as usize);
+}
diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs
new file mode 100644
index 000000000..62638f047
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_bits.rs
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![warn(clippy::manual_bits)]
+#![allow(
+ clippy::no_effect,
+ clippy::useless_conversion,
+ path_statements,
+ unused_must_use,
+ clippy::unnecessary_operation
+)]
+
+use std::mem::{size_of, size_of_val};
+
+fn main() {
+ size_of::<i8>() * 8;
+ size_of::<i16>() * 8;
+ size_of::<i32>() * 8;
+ size_of::<i64>() * 8;
+ size_of::<i128>() * 8;
+ size_of::<isize>() * 8;
+
+ size_of::<u8>() * 8;
+ size_of::<u16>() * 8;
+ size_of::<u32>() * 8;
+ size_of::<u64>() * 8;
+ size_of::<u128>() * 8;
+ size_of::<usize>() * 8;
+
+ 8 * size_of::<i8>();
+ 8 * size_of::<i16>();
+ 8 * size_of::<i32>();
+ 8 * size_of::<i64>();
+ 8 * size_of::<i128>();
+ 8 * size_of::<isize>();
+
+ 8 * size_of::<u8>();
+ 8 * size_of::<u16>();
+ 8 * size_of::<u32>();
+ 8 * size_of::<u64>();
+ 8 * size_of::<u128>();
+ 8 * size_of::<usize>();
+
+ size_of::<usize>() * 4;
+ 4 * size_of::<usize>();
+ size_of::<bool>() * 8;
+ 8 * size_of::<bool>();
+
+ size_of_val(&0u32) * 8;
+
+ type Word = u32;
+ size_of::<Word>() * 8;
+ type Bool = bool;
+ size_of::<Bool>() * 8;
+
+ let _: u32 = (size_of::<u128>() * 8) as u32;
+ let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+ let _ = (size_of::<u128>() * 8).pow(5);
+ let _ = &(size_of::<u128>() * 8);
+}
diff --git a/src/tools/clippy/tests/ui/manual_bits.stderr b/src/tools/clippy/tests/ui/manual_bits.stderr
new file mode 100644
index 000000000..69c591a20
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_bits.stderr
@@ -0,0 +1,178 @@
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:15:5
+ |
+LL | size_of::<i8>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
+ |
+ = note: `-D clippy::manual-bits` implied by `-D warnings`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:16:5
+ |
+LL | size_of::<i16>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:17:5
+ |
+LL | size_of::<i32>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:18:5
+ |
+LL | size_of::<i64>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:19:5
+ |
+LL | size_of::<i128>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:20:5
+ |
+LL | size_of::<isize>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:22:5
+ |
+LL | size_of::<u8>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:23:5
+ |
+LL | size_of::<u16>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:24:5
+ |
+LL | size_of::<u32>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:25:5
+ |
+LL | size_of::<u64>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:26:5
+ |
+LL | size_of::<u128>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:27:5
+ |
+LL | size_of::<usize>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:29:5
+ |
+LL | 8 * size_of::<i8>();
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:30:5
+ |
+LL | 8 * size_of::<i16>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:31:5
+ |
+LL | 8 * size_of::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:32:5
+ |
+LL | 8 * size_of::<i64>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:33:5
+ |
+LL | 8 * size_of::<i128>();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:34:5
+ |
+LL | 8 * size_of::<isize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:36:5
+ |
+LL | 8 * size_of::<u8>();
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:37:5
+ |
+LL | 8 * size_of::<u16>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:38:5
+ |
+LL | 8 * size_of::<u32>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:39:5
+ |
+LL | 8 * size_of::<u64>();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:40:5
+ |
+LL | 8 * size_of::<u128>();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:41:5
+ |
+LL | 8 * size_of::<usize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:51:5
+ |
+LL | size_of::<Word>() * 8;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:55:18
+ |
+LL | let _: u32 = (size_of::<u128>() * 8) as u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:56:18
+ |
+LL | let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:57:13
+ |
+LL | let _ = (size_of::<u128>() * 8).pow(5);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:58:14
+ |
+LL | let _ = &(size_of::<u128>() * 8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
+
+error: aborting due to 29 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.fixed b/src/tools/clippy/tests/ui/manual_filter_map.fixed
new file mode 100644
index 000000000..4936dc9b2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_filter_map.fixed
@@ -0,0 +1,121 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::manual_filter_map)]
+#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+
+fn main() {
+ // is_some(), unwrap()
+ let _ = (0..).filter_map(|a| to_opt(a));
+
+ // ref pattern, expect()
+ let _ = (0..).filter_map(|a| to_opt(a));
+
+ // is_ok(), unwrap_or()
+ let _ = (0..).filter_map(|a| to_res(a).ok());
+
+ let _ = (1..5)
+ .filter_map(|y| *to_ref(to_opt(y)));
+ let _ = (1..5)
+ .filter_map(|y| *to_ref(to_opt(y)));
+
+ let _ = (1..5)
+ .filter_map(|y| to_ref(to_res(y)).ok());
+ let _ = (1..5)
+ .filter_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<String>>().find_map(|x| x.as_deref());
+ iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+ iter::<Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+ iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
+}
+
+fn no_lint() {
+ // no shared code
+ let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
+
+ // very close but different since filter() provides a reference
+ let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // similar but different
+ let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
+ let _ = (0..)
+ .filter(|n| to_opt(n).map(|n| n + 1).is_some())
+ .map(|a| to_opt(a).unwrap());
+}
+
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
+fn to_opt<T>(_: T) -> Option<T> {
+ unimplemented!()
+}
+
+fn to_res<T>(_: T) -> Result<T, ()> {
+ unimplemented!()
+}
+
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
+struct Issue8920<'a> {
+ option_field: Option<String>,
+ result_field: Result<String, ()>,
+ ref_field: Option<&'a usize>,
+}
+
+fn issue_8920() {
+ let mut vec = vec![Issue8920 {
+ option_field: Some(String::from("str")),
+ result_field: Ok(String::from("str")),
+ ref_field: Some(&1),
+ }];
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.option_field.clone());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.ref_field.cloned());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.ref_field.copied());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.result_field.clone().ok());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.result_field.as_ref().ok());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.result_field.as_deref().ok());
+
+ let _ = vec
+ .iter_mut()
+ .filter_map(|f| f.result_field.as_mut().ok());
+
+ let _ = vec
+ .iter_mut()
+ .filter_map(|f| f.result_field.as_deref_mut().ok());
+
+ let _ = vec
+ .iter()
+ .filter_map(|f| f.result_field.to_owned().ok());
+}
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.rs b/src/tools/clippy/tests/ui/manual_filter_map.rs
new file mode 100644
index 000000000..8c67e827b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_filter_map.rs
@@ -0,0 +1,134 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::manual_filter_map)]
+#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+
+fn main() {
+ // is_some(), unwrap()
+ let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // ref pattern, expect()
+ let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
+
+ // is_ok(), unwrap_or()
+ let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+ let _ = (1..5)
+ .filter(|&x| to_ref(to_opt(x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+ let _ = (1..5)
+ .filter(|x| to_ref(to_opt(*x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+
+ let _ = (1..5)
+ .filter(|&x| to_ref(to_res(x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+ let _ = (1..5)
+ .filter(|x| to_ref(to_res(*x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+ iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+}
+
+fn no_lint() {
+ // no shared code
+ let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
+
+ // very close but different since filter() provides a reference
+ let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // similar but different
+ let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
+ let _ = (0..)
+ .filter(|n| to_opt(n).map(|n| n + 1).is_some())
+ .map(|a| to_opt(a).unwrap());
+}
+
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
+fn to_opt<T>(_: T) -> Option<T> {
+ unimplemented!()
+}
+
+fn to_res<T>(_: T) -> Result<T, ()> {
+ unimplemented!()
+}
+
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
+struct Issue8920<'a> {
+ option_field: Option<String>,
+ result_field: Result<String, ()>,
+ ref_field: Option<&'a usize>,
+}
+
+fn issue_8920() {
+ let mut vec = vec![Issue8920 {
+ option_field: Some(String::from("str")),
+ result_field: Ok(String::from("str")),
+ ref_field: Some(&1),
+ }];
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.option_field.is_some())
+ .map(|f| f.option_field.clone().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.ref_field.is_some())
+ .map(|f| f.ref_field.cloned().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.ref_field.is_some())
+ .map(|f| f.ref_field.copied().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.clone().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_ref().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_deref().unwrap());
+
+ let _ = vec
+ .iter_mut()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_mut().unwrap());
+
+ let _ = vec
+ .iter_mut()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_deref_mut().unwrap());
+
+ let _ = vec
+ .iter()
+ .filter(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.to_owned().unwrap());
+}
diff --git a/src/tools/clippy/tests/ui/manual_filter_map.stderr b/src/tools/clippy/tests/ui/manual_filter_map.stderr
new file mode 100644
index 000000000..6e5bbe8f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_filter_map.stderr
@@ -0,0 +1,194 @@
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:8:19
+ |
+LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
+ |
+ = note: `-D clippy::manual-filter-map` implied by `-D warnings`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:11:19
+ |
+LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:14:19
+ |
+LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:17:10
+ |
+LL | .filter(|&x| to_ref(to_opt(x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:20:10
+ |
+LL | .filter(|x| to_ref(to_opt(*x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:24:10
+ |
+LL | .filter(|&x| to_ref(to_res(x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:27:10
+ |
+LL | .filter(|x| to_ref(to_res(*x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:33:27
+ |
+LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+ |
+ = note: `-D clippy::manual-find-map` implied by `-D warnings`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:34:28
+ |
+LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:35:31
+ |
+LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:36:31
+ |
+LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:38:30
+ |
+LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:39:31
+ |
+LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:40:32
+ |
+LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:41:31
+ |
+LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:42:32
+ |
+LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:43:35
+ |
+LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:44:35
+ |
+LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:92:10
+ |
+LL | .filter(|f| f.option_field.is_some())
+ | __________^
+LL | | .map(|f| f.option_field.clone().unwrap());
+ | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:97:10
+ |
+LL | .filter(|f| f.ref_field.is_some())
+ | __________^
+LL | | .map(|f| f.ref_field.cloned().unwrap());
+ | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:102:10
+ |
+LL | .filter(|f| f.ref_field.is_some())
+ | __________^
+LL | | .map(|f| f.ref_field.copied().unwrap());
+ | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:107:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.clone().unwrap());
+ | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:112:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_ref().unwrap());
+ | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:117:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_deref().unwrap());
+ | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:122:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_mut().unwrap());
+ | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:127:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_deref_mut().unwrap());
+ | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:132:10
+ |
+LL | .filter(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.to_owned().unwrap());
+ | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
+
+error: aborting due to 27 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_find.rs b/src/tools/clippy/tests/ui/manual_find.rs
new file mode 100644
index 000000000..257fe045f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find.rs
@@ -0,0 +1,22 @@
+#![allow(unused)]
+#![warn(clippy::manual_find)]
+
+fn vec_string(strings: Vec<String>) -> Option<String> {
+ for s in strings {
+ if s == String::new() {
+ return Some(s);
+ }
+ }
+ None
+}
+
+fn tuple(arr: Vec<(String, i32)>) -> Option<String> {
+ for (s, _) in arr {
+ if s == String::new() {
+ return Some(s);
+ }
+ }
+ None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find.stderr b/src/tools/clippy/tests/ui/manual_find.stderr
new file mode 100644
index 000000000..da0fd4aae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find.stderr
@@ -0,0 +1,29 @@
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find.rs:5:5
+ |
+LL | / for s in strings {
+LL | | if s == String::new() {
+LL | | return Some(s);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `strings.into_iter().find(|s| s == String::new())`
+ |
+ = note: `-D clippy::manual-find` implied by `-D warnings`
+ = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find.rs:14:5
+ |
+LL | / for (s, _) in arr {
+LL | | if s == String::new() {
+LL | | return Some(s);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().map(|(s, _)| s).find(|s| s == String::new())`
+ |
+ = note: you may need to dereference some variables
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.fixed b/src/tools/clippy/tests/ui/manual_find_fixable.fixed
new file mode 100644
index 000000000..36d1644c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.fixed
@@ -0,0 +1,182 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_return)]
+#![warn(clippy::manual_find)]
+
+use std::collections::HashMap;
+
+const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3];
+
+fn lookup(n: u32) -> Option<u32> {
+ ARRAY.iter().find(|&&v| v == n).copied()
+}
+
+fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
+ arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)
+}
+
+struct Data {
+ name: String,
+ is_true: bool,
+}
+fn with_struct(arr: Vec<Data>) -> Option<Data> {
+ arr.into_iter().find(|el| el.name.len() == 10)
+}
+
+struct Tuple(usize, usize);
+fn with_tuple_struct(arr: Vec<Tuple>) -> Option<usize> {
+ arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3)
+}
+
+struct A;
+impl A {
+ fn should_keep(&self) -> bool {
+ true
+ }
+}
+fn with_method_call(arr: Vec<A>) -> Option<A> {
+ arr.into_iter().find(|el| el.should_keep())
+}
+
+fn with_closure(arr: Vec<u32>) -> Option<u32> {
+ let f = |el: u32| -> u32 { el + 10 };
+ arr.into_iter().find(|&el| f(el) == 20)
+}
+
+fn with_closure2(arr: HashMap<String, i32>) -> Option<i32> {
+ let f = |el: i32| -> bool { el == 10 };
+ arr.values().find(|&&el| f(el)).copied()
+}
+
+fn with_bool(arr: Vec<Data>) -> Option<Data> {
+ arr.into_iter().find(|el| el.is_true)
+}
+
+fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
+ for v in arr {
+ if v == 1 {
+ println!("side effect");
+ return Some(v);
+ }
+ }
+ None
+}
+
+fn with_else(arr: Vec<u32>) -> Option<u32> {
+ for el in arr {
+ if el % 2 == 0 {
+ return Some(el);
+ } else {
+ println!("{}", el);
+ }
+ }
+ None
+}
+
+fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option<u8> {
+ v.into_iter().map(|(_, &x)| x).find(|&x| x > 10)
+}
+
+fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option<u8> {
+ v.iter().map(|&(_, &x)| x).find(|&x| x > 10)
+}
+
+fn explicit_ret(arr: Vec<i32>) -> Option<i32> {
+ arr.into_iter().find(|&x| x >= 5)
+}
+
+fn plus_one(a: i32) -> Option<i32> {
+ Some(a + 1)
+}
+fn fn_instead_of_some(a: &[i32]) -> Option<i32> {
+ for &x in a {
+ if x == 1 {
+ return plus_one(x);
+ }
+ }
+ None
+}
+
+fn for_in_condition(a: &[i32], b: bool) -> Option<i32> {
+ if b {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+ }
+ None
+}
+
+fn intermediate_statements(a: &[i32]) -> Option<i32> {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+
+ println!("side effect");
+
+ None
+}
+
+fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option<i32> {
+ for (x, mut s) in arr {
+ if x == 1 && s.as_mut_str().len() == 2 {
+ return Some(x);
+ }
+ }
+ None
+}
+
+fn as_closure() {
+ #[rustfmt::skip]
+ let f = |arr: Vec<i32>| -> Option<i32> {
+ arr.into_iter().find(|&x| x < 1)
+ };
+}
+
+fn in_block(a: &[i32]) -> Option<i32> {
+ let should_be_none = {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+ None
+ };
+
+ assert!(should_be_none.is_none());
+
+ should_be_none
+}
+
+// Not handled yet
+fn mut_binding(v: Vec<String>) -> Option<String> {
+ for mut s in v {
+ if s.as_mut_str().len() > 1 {
+ return Some(s);
+ }
+ }
+ None
+}
+
+fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> {
+ for a @ [first, ..] in v {
+ if a[12] == first {
+ return Some(a);
+ }
+ }
+ None
+}
+
+fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
+ for (a, n) in v {
+ if a == n {
+ return Some(a);
+ }
+ }
+ None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.rs b/src/tools/clippy/tests/ui/manual_find_fixable.rs
new file mode 100644
index 000000000..ed277ddaa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.rs
@@ -0,0 +1,242 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_return)]
+#![warn(clippy::manual_find)]
+
+use std::collections::HashMap;
+
+const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3];
+
+fn lookup(n: u32) -> Option<u32> {
+ for &v in ARRAY {
+ if v == n {
+ return Some(v);
+ }
+ }
+ None
+}
+
+fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
+ for (a, _) in arr {
+ if a % 2 == 0 {
+ return Some(a);
+ }
+ }
+ None
+}
+
+struct Data {
+ name: String,
+ is_true: bool,
+}
+fn with_struct(arr: Vec<Data>) -> Option<Data> {
+ for el in arr {
+ if el.name.len() == 10 {
+ return Some(el);
+ }
+ }
+ None
+}
+
+struct Tuple(usize, usize);
+fn with_tuple_struct(arr: Vec<Tuple>) -> Option<usize> {
+ for Tuple(a, _) in arr {
+ if a >= 3 {
+ return Some(a);
+ }
+ }
+ None
+}
+
+struct A;
+impl A {
+ fn should_keep(&self) -> bool {
+ true
+ }
+}
+fn with_method_call(arr: Vec<A>) -> Option<A> {
+ for el in arr {
+ if el.should_keep() {
+ return Some(el);
+ }
+ }
+ None
+}
+
+fn with_closure(arr: Vec<u32>) -> Option<u32> {
+ let f = |el: u32| -> u32 { el + 10 };
+ for el in arr {
+ if f(el) == 20 {
+ return Some(el);
+ }
+ }
+ None
+}
+
+fn with_closure2(arr: HashMap<String, i32>) -> Option<i32> {
+ let f = |el: i32| -> bool { el == 10 };
+ for &el in arr.values() {
+ if f(el) {
+ return Some(el);
+ }
+ }
+ None
+}
+
+fn with_bool(arr: Vec<Data>) -> Option<Data> {
+ for el in arr {
+ if el.is_true {
+ return Some(el);
+ }
+ }
+ None
+}
+
+fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
+ for v in arr {
+ if v == 1 {
+ println!("side effect");
+ return Some(v);
+ }
+ }
+ None
+}
+
+fn with_else(arr: Vec<u32>) -> Option<u32> {
+ for el in arr {
+ if el % 2 == 0 {
+ return Some(el);
+ } else {
+ println!("{}", el);
+ }
+ }
+ None
+}
+
+fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option<u8> {
+ for (_, &x) in v {
+ if x > 10 {
+ return Some(x);
+ }
+ }
+ None
+}
+
+fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option<u8> {
+ for &(_, &x) in v {
+ if x > 10 {
+ return Some(x);
+ }
+ }
+ None
+}
+
+fn explicit_ret(arr: Vec<i32>) -> Option<i32> {
+ for x in arr {
+ if x >= 5 {
+ return Some(x);
+ }
+ }
+ return None;
+}
+
+fn plus_one(a: i32) -> Option<i32> {
+ Some(a + 1)
+}
+fn fn_instead_of_some(a: &[i32]) -> Option<i32> {
+ for &x in a {
+ if x == 1 {
+ return plus_one(x);
+ }
+ }
+ None
+}
+
+fn for_in_condition(a: &[i32], b: bool) -> Option<i32> {
+ if b {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+ }
+ None
+}
+
+fn intermediate_statements(a: &[i32]) -> Option<i32> {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+
+ println!("side effect");
+
+ None
+}
+
+fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option<i32> {
+ for (x, mut s) in arr {
+ if x == 1 && s.as_mut_str().len() == 2 {
+ return Some(x);
+ }
+ }
+ None
+}
+
+fn as_closure() {
+ #[rustfmt::skip]
+ let f = |arr: Vec<i32>| -> Option<i32> {
+ for x in arr {
+ if x < 1 {
+ return Some(x);
+ }
+ }
+ None
+ };
+}
+
+fn in_block(a: &[i32]) -> Option<i32> {
+ let should_be_none = {
+ for &x in a {
+ if x == 1 {
+ return Some(x);
+ }
+ }
+ None
+ };
+
+ assert!(should_be_none.is_none());
+
+ should_be_none
+}
+
+// Not handled yet
+fn mut_binding(v: Vec<String>) -> Option<String> {
+ for mut s in v {
+ if s.as_mut_str().len() > 1 {
+ return Some(s);
+ }
+ }
+ None
+}
+
+fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> {
+ for a @ [first, ..] in v {
+ if a[12] == first {
+ return Some(a);
+ }
+ }
+ None
+}
+
+fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
+ for (a, n) in v {
+ if a == n {
+ return Some(a);
+ }
+ }
+ None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.stderr b/src/tools/clippy/tests/ui/manual_find_fixable.stderr
new file mode 100644
index 000000000..dbc4ff69a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.stderr
@@ -0,0 +1,142 @@
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:11:5
+ |
+LL | / for &v in ARRAY {
+LL | | if v == n {
+LL | | return Some(v);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `ARRAY.iter().find(|&&v| v == n).copied()`
+ |
+ = note: `-D clippy::manual-find` implied by `-D warnings`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:20:5
+ |
+LL | / for (a, _) in arr {
+LL | | if a % 2 == 0 {
+LL | | return Some(a);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:33:5
+ |
+LL | / for el in arr {
+LL | | if el.name.len() == 10 {
+LL | | return Some(el);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.name.len() == 10)`
+ |
+ = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:43:5
+ |
+LL | / for Tuple(a, _) in arr {
+LL | | if a >= 3 {
+LL | | return Some(a);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:58:5
+ |
+LL | / for el in arr {
+LL | | if el.should_keep() {
+LL | | return Some(el);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.should_keep())`
+ |
+ = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:68:5
+ |
+LL | / for el in arr {
+LL | | if f(el) == 20 {
+LL | | return Some(el);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().find(|&el| f(el) == 20)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:78:5
+ |
+LL | / for &el in arr.values() {
+LL | | if f(el) {
+LL | | return Some(el);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.values().find(|&&el| f(el)).copied()`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:87:5
+ |
+LL | / for el in arr {
+LL | | if el.is_true {
+LL | | return Some(el);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.is_true)`
+ |
+ = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:117:5
+ |
+LL | / for (_, &x) in v {
+LL | | if x > 10 {
+LL | | return Some(x);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `v.into_iter().map(|(_, &x)| x).find(|&x| x > 10)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:126:5
+ |
+LL | / for &(_, &x) in v {
+LL | | if x > 10 {
+LL | | return Some(x);
+LL | | }
+LL | | }
+LL | | None
+ | |________^ help: replace with an iterator: `v.iter().map(|&(_, &x)| x).find(|&x| x > 10)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:135:5
+ |
+LL | / for x in arr {
+LL | | if x >= 5 {
+LL | | return Some(x);
+LL | | }
+LL | | }
+LL | | return None;
+ | |________________^ help: replace with an iterator: `arr.into_iter().find(|&x| x >= 5)`
+
+error: manual implementation of `Iterator::find`
+ --> $DIR/manual_find_fixable.rs:190:9
+ |
+LL | / for x in arr {
+LL | | if x < 1 {
+LL | | return Some(x);
+LL | | }
+LL | | }
+LL | | None
+ | |____________^ help: replace with an iterator: `arr.into_iter().find(|&x| x < 1)`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_find_map.fixed b/src/tools/clippy/tests/ui/manual_find_map.fixed
new file mode 100644
index 000000000..54302bece
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_map.fixed
@@ -0,0 +1,124 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::manual_find_map)]
+#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+
+fn main() {
+ // is_some(), unwrap()
+ let _ = (0..).find_map(|a| to_opt(a));
+
+ // ref pattern, expect()
+ let _ = (0..).find_map(|a| to_opt(a));
+
+ // is_ok(), unwrap_or()
+ let _ = (0..).find_map(|a| to_res(a).ok());
+
+ let _ = (1..5)
+ .find_map(|y| *to_ref(to_opt(y)));
+ let _ = (1..5)
+ .find_map(|y| *to_ref(to_opt(y)));
+
+ let _ = (1..5)
+ .find_map(|y| to_ref(to_res(y)).ok());
+ let _ = (1..5)
+ .find_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<u8>>().find_map(|x| x);
+ iter::<&Option<u8>>().find_map(|x| *x);
+ iter::<&&Option<u8>>().find_map(|x| **x);
+ iter::<Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<String>>().find_map(|x| x.as_deref());
+ iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+ iter::<Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+ iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
+}
+
+fn no_lint() {
+ // no shared code
+ let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
+
+ // very close but different since filter() provides a reference
+ let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // similar but different
+ let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
+ let _ = (0..)
+ .find(|n| to_opt(n).map(|n| n + 1).is_some())
+ .map(|a| to_opt(a).unwrap());
+}
+
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
+fn to_opt<T>(_: T) -> Option<T> {
+ unimplemented!()
+}
+
+fn to_res<T>(_: T) -> Result<T, ()> {
+ unimplemented!()
+}
+
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
+struct Issue8920<'a> {
+ option_field: Option<String>,
+ result_field: Result<String, ()>,
+ ref_field: Option<&'a usize>,
+}
+
+fn issue_8920() {
+ let mut vec = vec![Issue8920 {
+ option_field: Some(String::from("str")),
+ result_field: Ok(String::from("str")),
+ ref_field: Some(&1),
+ }];
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.option_field.clone());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.ref_field.cloned());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.ref_field.copied());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.result_field.clone().ok());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.result_field.as_ref().ok());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.result_field.as_deref().ok());
+
+ let _ = vec
+ .iter_mut()
+ .find_map(|f| f.result_field.as_mut().ok());
+
+ let _ = vec
+ .iter_mut()
+ .find_map(|f| f.result_field.as_deref_mut().ok());
+
+ let _ = vec
+ .iter()
+ .find_map(|f| f.result_field.to_owned().ok());
+}
diff --git a/src/tools/clippy/tests/ui/manual_find_map.rs b/src/tools/clippy/tests/ui/manual_find_map.rs
new file mode 100644
index 000000000..afcc1825a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_map.rs
@@ -0,0 +1,137 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::manual_find_map)]
+#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
+
+fn main() {
+ // is_some(), unwrap()
+ let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // ref pattern, expect()
+ let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
+
+ // is_ok(), unwrap_or()
+ let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+ let _ = (1..5)
+ .find(|&x| to_ref(to_opt(x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+ let _ = (1..5)
+ .find(|x| to_ref(to_opt(*x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+
+ let _ = (1..5)
+ .find(|&x| to_ref(to_res(x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+ let _ = (1..5)
+ .find(|x| to_ref(to_res(*x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+ iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+}
+
+fn no_lint() {
+ // no shared code
+ let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
+
+ // very close but different since filter() provides a reference
+ let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
+
+ // similar but different
+ let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
+ let _ = (0..)
+ .find(|n| to_opt(n).map(|n| n + 1).is_some())
+ .map(|a| to_opt(a).unwrap());
+}
+
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
+fn to_opt<T>(_: T) -> Option<T> {
+ unimplemented!()
+}
+
+fn to_res<T>(_: T) -> Result<T, ()> {
+ unimplemented!()
+}
+
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
+struct Issue8920<'a> {
+ option_field: Option<String>,
+ result_field: Result<String, ()>,
+ ref_field: Option<&'a usize>,
+}
+
+fn issue_8920() {
+ let mut vec = vec![Issue8920 {
+ option_field: Some(String::from("str")),
+ result_field: Ok(String::from("str")),
+ ref_field: Some(&1),
+ }];
+
+ let _ = vec
+ .iter()
+ .find(|f| f.option_field.is_some())
+ .map(|f| f.option_field.clone().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.ref_field.is_some())
+ .map(|f| f.ref_field.cloned().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.ref_field.is_some())
+ .map(|f| f.ref_field.copied().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.clone().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_ref().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_deref().unwrap());
+
+ let _ = vec
+ .iter_mut()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_mut().unwrap());
+
+ let _ = vec
+ .iter_mut()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.as_deref_mut().unwrap());
+
+ let _ = vec
+ .iter()
+ .find(|f| f.result_field.is_ok())
+ .map(|f| f.result_field.to_owned().unwrap());
+}
diff --git a/src/tools/clippy/tests/ui/manual_find_map.stderr b/src/tools/clippy/tests/ui/manual_find_map.stderr
new file mode 100644
index 000000000..c1ac499f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_map.stderr
@@ -0,0 +1,210 @@
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:8:19
+ |
+LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
+ |
+ = note: `-D clippy::manual-find-map` implied by `-D warnings`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:11:19
+ |
+LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:14:19
+ |
+LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:17:10
+ |
+LL | .find(|&x| to_ref(to_opt(x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:20:10
+ |
+LL | .find(|x| to_ref(to_opt(*x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:24:10
+ |
+LL | .find(|&x| to_ref(to_res(x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:27:10
+ |
+LL | .find(|x| to_ref(to_res(*x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:33:26
+ |
+LL | iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:34:27
+ |
+LL | iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:35:28
+ |
+LL | iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:36:27
+ |
+LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:37:28
+ |
+LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:38:31
+ |
+LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:39:31
+ |
+LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:41:30
+ |
+LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:42:31
+ |
+LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:43:32
+ |
+LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:44:31
+ |
+LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:45:32
+ |
+LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:46:35
+ |
+LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:47:35
+ |
+LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:95:10
+ |
+LL | .find(|f| f.option_field.is_some())
+ | __________^
+LL | | .map(|f| f.option_field.clone().unwrap());
+ | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:100:10
+ |
+LL | .find(|f| f.ref_field.is_some())
+ | __________^
+LL | | .map(|f| f.ref_field.cloned().unwrap());
+ | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:105:10
+ |
+LL | .find(|f| f.ref_field.is_some())
+ | __________^
+LL | | .map(|f| f.ref_field.copied().unwrap());
+ | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:110:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.clone().unwrap());
+ | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:115:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_ref().unwrap());
+ | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:120:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_deref().unwrap());
+ | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:125:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_mut().unwrap());
+ | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:130:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.as_deref_mut().unwrap());
+ | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:135:10
+ |
+LL | .find(|f| f.result_field.is_ok())
+ | __________^
+LL | | .map(|f| f.result_field.to_owned().unwrap());
+ | |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())`
+
+error: aborting due to 30 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs
new file mode 100644
index 000000000..d922593bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_flatten.rs
@@ -0,0 +1,125 @@
+#![warn(clippy::manual_flatten)]
+#![allow(clippy::useless_vec)]
+
+fn main() {
+ // Test for loop over implicitly adjusted `Iterator` with `if let` expression
+ let x = vec![Some(1), Some(2), Some(3)];
+ for n in x {
+ if let Some(y) = n {
+ println!("{}", y);
+ }
+ }
+
+ // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement
+ let y: Vec<Result<i32, i32>> = vec![];
+ for n in y.clone() {
+ if let Ok(n) = n {
+ println!("{}", n);
+ };
+ }
+
+ // Test for loop over by reference
+ for n in &y {
+ if let Ok(n) = n {
+ println!("{}", n);
+ }
+ }
+
+ // Test for loop over an implicit reference
+ let z = &y;
+ for n in z {
+ if let Ok(n) = n {
+ println!("{}", n);
+ }
+ }
+
+ // Test for loop over `Iterator` with `if let` expression
+ let z = vec![Some(1), Some(2), Some(3)];
+ let z = z.iter();
+ for n in z {
+ if let Some(m) = n {
+ println!("{}", m);
+ }
+ }
+
+ // Using the `None` variant should not trigger the lint
+ // Note: for an autofixable suggestion, the binding in the for loop has to take the
+ // name of the binding in the `if let`
+ let z = vec![Some(1), Some(2), Some(3)];
+ for n in z {
+ if n.is_none() {
+ println!("Nada.");
+ }
+ }
+
+ // Using the `Err` variant should not trigger the lint
+ for n in y.clone() {
+ if let Err(e) = n {
+ println!("Oops: {}!", e);
+ }
+ }
+
+ // Having an else clause should not trigger the lint
+ for n in y.clone() {
+ if let Ok(n) = n {
+ println!("{}", n);
+ } else {
+ println!("Oops!");
+ }
+ }
+
+ let vec_of_ref = vec![&Some(1)];
+ for n in &vec_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
+ let vec_of_ref = &vec_of_ref;
+ for n in vec_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
+ let slice_of_ref = &[&Some(1)];
+ for n in slice_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
+ struct Test {
+ a: usize,
+ }
+
+ let mut vec_of_struct = [Some(Test { a: 1 }), None];
+
+ // Usage of `if let` expression should not trigger lint
+ for n in vec_of_struct.iter_mut() {
+ if let Some(z) = n {
+ *n = None;
+ }
+ }
+
+ // Using manual flatten should not trigger the lint
+ for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
+ println!("{}", n);
+ }
+
+ run_unformatted_tests();
+}
+
+#[rustfmt::skip]
+fn run_unformatted_tests() {
+ // Skip rustfmt here on purpose so the suggestion does not fit in one line
+ for n in vec![
+ Some(1),
+ Some(2),
+ Some(3)
+ ].iter() {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr
new file mode 100644
index 000000000..da053c056
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_flatten.stderr
@@ -0,0 +1,199 @@
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:7:5
+ |
+LL | for n in x {
+ | ^ - help: try: `x.into_iter().flatten()`
+ | _____|
+ | |
+LL | | if let Some(y) = n {
+LL | | println!("{}", y);
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::manual-flatten` implied by `-D warnings`
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:8:9
+ |
+LL | / if let Some(y) = n {
+LL | | println!("{}", y);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:15:5
+ |
+LL | for n in y.clone() {
+ | ^ --------- help: try: `y.clone().into_iter().flatten()`
+ | _____|
+ | |
+LL | | if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:16:9
+ |
+LL | / if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | };
+ | |_________^
+
+error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:22:5
+ |
+LL | for n in &y {
+ | ^ -- help: try: `y.iter().flatten()`
+ | _____|
+ | |
+LL | | if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:23:9
+ |
+LL | / if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:30:5
+ |
+LL | for n in z {
+ | ^ - help: try: `z.iter().flatten()`
+ | _____|
+ | |
+LL | | if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:31:9
+ |
+LL | / if let Ok(n) = n {
+LL | | println!("{}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:39:5
+ |
+LL | for n in z {
+ | ^ - help: try: `z.flatten()`
+ | _____|
+ | |
+LL | | if let Some(m) = n {
+LL | | println!("{}", m);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:40:9
+ |
+LL | / if let Some(m) = n {
+LL | | println!("{}", m);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:72:5
+ |
+LL | for n in &vec_of_ref {
+ | ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:73:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:79:5
+ |
+LL | for n in vec_of_ref {
+ | ^ ---------- help: try: `vec_of_ref.iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:80:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:86:5
+ |
+LL | for n in slice_of_ref {
+ | ^ ------------ help: try: `slice_of_ref.iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:87:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:116:5
+ |
+LL | / for n in vec![
+LL | | Some(1),
+LL | | Some(2),
+LL | | Some(3)
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: remove the `if let` statement in the for loop and then...
+ --> $DIR/manual_flatten.rs:121:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+help: try
+ |
+LL ~ for n in vec![
+LL + Some(1),
+LL + Some(2),
+LL + Some(3)
+LL ~ ].iter().flatten() {
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_map_option.fixed b/src/tools/clippy/tests/ui/manual_map_option.fixed
new file mode 100644
index 000000000..a59da4ae1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option.fixed
@@ -0,0 +1,157 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(
+ clippy::no_effect,
+ clippy::map_identity,
+ clippy::unit_arg,
+ clippy::match_ref_pats,
+ clippy::redundant_pattern_matching,
+ clippy::for_loops_over_fallibles,
+ dead_code
+)]
+
+fn main() {
+ Some(0).map(|_| 2);
+
+ Some(0).map(|x| x + 1);
+
+ Some("").map(|x| x.is_empty());
+
+ Some(0).map(|x| !x);
+
+ #[rustfmt::skip]
+ Some(0).map(std::convert::identity);
+
+ Some(&String::new()).map(|x| str::len(x));
+
+ match Some(0) {
+ Some(x) if false => Some(x + 1),
+ _ => None,
+ };
+
+ Some([0, 1]).as_ref().map(|x| x[0]);
+
+ Some(0).map(|x| x * 2);
+
+ Some(String::new()).as_ref().map(|x| x.is_empty());
+
+ Some(String::new()).as_ref().map(|x| x.len());
+
+ Some(0).map(|x| x + x);
+
+ #[warn(clippy::option_map_unit_fn)]
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+
+ #[allow(clippy::option_map_unit_fn)]
+ {
+ Some(String::new()).as_mut().map(|x| x.push_str(""));
+ }
+
+ Some(String::new()).as_ref().map(|x| x.len());
+
+ Some(String::new()).as_ref().map(|x| x.is_empty());
+
+ Some((0, 1, 2)).map(|(x, y, z)| x + y + z);
+
+ Some([1, 2, 3]).map(|[first, ..]| first);
+
+ Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x));
+
+ match Some((String::new(), 0)) {
+ Some((ref x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some(Some(0)) {
+ Some(Some(_)) | Some(None) => Some(0),
+ None => None,
+ };
+
+ match Some(Some((0, 1))) {
+ Some(Some((x, 1))) => Some(x),
+ _ => None,
+ };
+
+ // #6795
+ fn f1() -> Result<(), ()> {
+ let _ = match Some(Ok(())) {
+ Some(x) => Some(x?),
+ None => None,
+ };
+ Ok(())
+ }
+
+ for &x in Some(Some(true)).iter() {
+ let _ = match x {
+ Some(x) => Some(if x { continue } else { x }),
+ None => None,
+ };
+ }
+
+ // #6797
+ let x1 = (Some(String::new()), 0);
+ let x2 = x1.0;
+ match x2 {
+ Some(x) => Some((x, x1.1)),
+ None => None,
+ };
+
+ struct S1 {
+ x: Option<String>,
+ y: u32,
+ }
+ impl S1 {
+ fn f(self) -> Option<(String, u32)> {
+ match self.x {
+ Some(x) => Some((x, self.y)),
+ None => None,
+ }
+ }
+ }
+
+ // #6811
+ Some(0).map(|x| vec![x]);
+
+ option_env!("").map(String::from);
+
+ // #6819
+ async fn f2(x: u32) -> u32 {
+ x
+ }
+
+ async fn f3() {
+ match Some(0) {
+ Some(x) => Some(f2(x).await),
+ None => None,
+ };
+ }
+
+ // #6847
+ if let Some(_) = Some(0) {
+ Some(0)
+ } else { Some(0).map(|x| x + 1) };
+
+ if true {
+ Some(0)
+ } else { Some(0).map(|x| x + 1) };
+
+ // #6967
+ const fn f4() {
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ None => None,
+ };
+ }
+
+ // #7077
+ let s = &String::new();
+ #[allow(clippy::needless_match)]
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs
new file mode 100644
index 000000000..0bdbefa51
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option.rs
@@ -0,0 +1,223 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(
+ clippy::no_effect,
+ clippy::map_identity,
+ clippy::unit_arg,
+ clippy::match_ref_pats,
+ clippy::redundant_pattern_matching,
+ clippy::for_loops_over_fallibles,
+ dead_code
+)]
+
+fn main() {
+ match Some(0) {
+ Some(_) => Some(2),
+ None::<u32> => None,
+ };
+
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ _ => None,
+ };
+
+ match Some("") {
+ Some(x) => Some(x.is_empty()),
+ None => None,
+ };
+
+ if let Some(x) = Some(0) {
+ Some(!x)
+ } else {
+ None
+ };
+
+ #[rustfmt::skip]
+ match Some(0) {
+ Some(x) => { Some(std::convert::identity(x)) }
+ None => { None }
+ };
+
+ match Some(&String::new()) {
+ Some(x) => Some(str::len(x)),
+ None => None,
+ };
+
+ match Some(0) {
+ Some(x) if false => Some(x + 1),
+ _ => None,
+ };
+
+ match &Some([0, 1]) {
+ Some(x) => Some(x[0]),
+ &None => None,
+ };
+
+ match &Some(0) {
+ &Some(x) => Some(x * 2),
+ None => None,
+ };
+
+ match Some(String::new()) {
+ Some(ref x) => Some(x.is_empty()),
+ _ => None,
+ };
+
+ match &&Some(String::new()) {
+ Some(x) => Some(x.len()),
+ _ => None,
+ };
+
+ match &&Some(0) {
+ &&Some(x) => Some(x + x),
+ &&_ => None,
+ };
+
+ #[warn(clippy::option_map_unit_fn)]
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+
+ #[allow(clippy::option_map_unit_fn)]
+ {
+ match &mut Some(String::new()) {
+ Some(x) => Some(x.push_str("")),
+ None => None,
+ };
+ }
+
+ match &mut Some(String::new()) {
+ Some(ref x) => Some(x.len()),
+ None => None,
+ };
+
+ match &mut &Some(String::new()) {
+ Some(x) => Some(x.is_empty()),
+ &mut _ => None,
+ };
+
+ match Some((0, 1, 2)) {
+ Some((x, y, z)) => Some(x + y + z),
+ None => None,
+ };
+
+ match Some([1, 2, 3]) {
+ Some([first, ..]) => Some(first),
+ None => None,
+ };
+
+ match &Some((String::new(), "test")) {
+ Some((x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some((String::new(), 0)) {
+ Some((ref x, y)) => Some((y, x)),
+ None => None,
+ };
+
+ match Some(Some(0)) {
+ Some(Some(_)) | Some(None) => Some(0),
+ None => None,
+ };
+
+ match Some(Some((0, 1))) {
+ Some(Some((x, 1))) => Some(x),
+ _ => None,
+ };
+
+ // #6795
+ fn f1() -> Result<(), ()> {
+ let _ = match Some(Ok(())) {
+ Some(x) => Some(x?),
+ None => None,
+ };
+ Ok(())
+ }
+
+ for &x in Some(Some(true)).iter() {
+ let _ = match x {
+ Some(x) => Some(if x { continue } else { x }),
+ None => None,
+ };
+ }
+
+ // #6797
+ let x1 = (Some(String::new()), 0);
+ let x2 = x1.0;
+ match x2 {
+ Some(x) => Some((x, x1.1)),
+ None => None,
+ };
+
+ struct S1 {
+ x: Option<String>,
+ y: u32,
+ }
+ impl S1 {
+ fn f(self) -> Option<(String, u32)> {
+ match self.x {
+ Some(x) => Some((x, self.y)),
+ None => None,
+ }
+ }
+ }
+
+ // #6811
+ match Some(0) {
+ Some(x) => Some(vec![x]),
+ None => None,
+ };
+
+ match option_env!("") {
+ Some(x) => Some(String::from(x)),
+ None => None,
+ };
+
+ // #6819
+ async fn f2(x: u32) -> u32 {
+ x
+ }
+
+ async fn f3() {
+ match Some(0) {
+ Some(x) => Some(f2(x).await),
+ None => None,
+ };
+ }
+
+ // #6847
+ if let Some(_) = Some(0) {
+ Some(0)
+ } else if let Some(x) = Some(0) {
+ Some(x + 1)
+ } else {
+ None
+ };
+
+ if true {
+ Some(0)
+ } else if let Some(x) = Some(0) {
+ Some(x + 1)
+ } else {
+ None
+ };
+
+ // #6967
+ const fn f4() {
+ match Some(0) {
+ Some(x) => Some(x + 1),
+ None => None,
+ };
+ }
+
+ // #7077
+ let s = &String::new();
+ #[allow(clippy::needless_match)]
+ let _: Option<&str> = match Some(s) {
+ Some(s) => Some(s),
+ None => None,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option.stderr b/src/tools/clippy/tests/ui/manual_map_option.stderr
new file mode 100644
index 000000000..cdc2c0e62
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option.stderr
@@ -0,0 +1,198 @@
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:15:5
+ |
+LL | / match Some(0) {
+LL | | Some(_) => Some(2),
+LL | | None::<u32> => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|_| 2)`
+ |
+ = note: `-D clippy::manual-map` implied by `-D warnings`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:20:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => Some(x + 1),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x + 1)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:25:5
+ |
+LL | / match Some("") {
+LL | | Some(x) => Some(x.is_empty()),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some("").map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:30:5
+ |
+LL | / if let Some(x) = Some(0) {
+LL | | Some(!x)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| !x)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:37:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => { Some(std::convert::identity(x)) }
+LL | | None => { None }
+LL | | };
+ | |_____^ help: try this: `Some(0).map(std::convert::identity)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:42:5
+ |
+LL | / match Some(&String::new()) {
+LL | | Some(x) => Some(str::len(x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:52:5
+ |
+LL | / match &Some([0, 1]) {
+LL | | Some(x) => Some(x[0]),
+LL | | &None => None,
+LL | | };
+ | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:57:5
+ |
+LL | / match &Some(0) {
+LL | | &Some(x) => Some(x * 2),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x * 2)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:62:5
+ |
+LL | / match Some(String::new()) {
+LL | | Some(ref x) => Some(x.is_empty()),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:67:5
+ |
+LL | / match &&Some(String::new()) {
+LL | | Some(x) => Some(x.len()),
+LL | | _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:72:5
+ |
+LL | / match &&Some(0) {
+LL | | &&Some(x) => Some(x + x),
+LL | | &&_ => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| x + x)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:85:9
+ |
+LL | / match &mut Some(String::new()) {
+LL | | Some(x) => Some(x.push_str("")),
+LL | | None => None,
+LL | | };
+ | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:91:5
+ |
+LL | / match &mut Some(String::new()) {
+LL | | Some(ref x) => Some(x.len()),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:96:5
+ |
+LL | / match &mut &Some(String::new()) {
+LL | | Some(x) => Some(x.is_empty()),
+LL | | &mut _ => None,
+LL | | };
+ | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:101:5
+ |
+LL | / match Some((0, 1, 2)) {
+LL | | Some((x, y, z)) => Some(x + y + z),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:106:5
+ |
+LL | / match Some([1, 2, 3]) {
+LL | | Some([first, ..]) => Some(first),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:111:5
+ |
+LL | / match &Some((String::new(), "test")) {
+LL | | Some((x, y)) => Some((y, x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:169:5
+ |
+LL | / match Some(0) {
+LL | | Some(x) => Some(vec![x]),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| vec![x])`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:174:5
+ |
+LL | / match option_env!("") {
+LL | | Some(x) => Some(String::from(x)),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `option_env!("").map(String::from)`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:194:12
+ |
+LL | } else if let Some(x) = Some(0) {
+ | ____________^
+LL | | Some(x + 1)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:202:12
+ |
+LL | } else if let Some(x) = Some(0) {
+ | ____________^
+LL | | Some(x + 1)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
new file mode 100644
index 000000000..ebf3f8cab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
@@ -0,0 +1,60 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::toplevel_ref_arg)]
+
+fn main() {
+ // Lint. `y` is declared within the arm, so it isn't captured by the map closure
+ let _ = Some(0).map(|x| {
+ let y = (String::new(), String::new());
+ (x, y.0)
+ });
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+ // closure
+ let s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some((x.clone(), s)),
+ None => None,
+ };
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+ // closure
+ let s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some({
+ let clone = x.clone();
+ let s = || s;
+ (clone, s())
+ }),
+ None => None,
+ };
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
+ // reference by the map closure
+ let mut s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some({
+ let clone = x.clone();
+ let ref mut s = s;
+ (clone, s)
+ }),
+ None => None,
+ };
+
+ // Lint. `s` is captured by reference, so no lifetime issues.
+ let s = Some(String::new());
+ let _ = s.as_ref().map(|x| {
+ if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+ });
+
+ // Issue #7820
+ unsafe fn f(x: u32) -> u32 {
+ x
+ }
+ unsafe {
+ let _ = Some(0).map(|x| f(x));
+ }
+ let _ = Some(0).map(|x| unsafe { f(x) });
+ let _ = Some(0).map(|x| unsafe { f(x) });
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs
new file mode 100644
index 000000000..1382d9af0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs
@@ -0,0 +1,75 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::toplevel_ref_arg)]
+
+fn main() {
+ // Lint. `y` is declared within the arm, so it isn't captured by the map closure
+ let _ = match Some(0) {
+ Some(x) => Some({
+ let y = (String::new(), String::new());
+ (x, y.0)
+ }),
+ None => None,
+ };
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+ // closure
+ let s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some((x.clone(), s)),
+ None => None,
+ };
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+ // closure
+ let s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some({
+ let clone = x.clone();
+ let s = || s;
+ (clone, s())
+ }),
+ None => None,
+ };
+
+ // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
+ // reference by the map closure
+ let mut s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some({
+ let clone = x.clone();
+ let ref mut s = s;
+ (clone, s)
+ }),
+ None => None,
+ };
+
+ // Lint. `s` is captured by reference, so no lifetime issues.
+ let s = Some(String::new());
+ let _ = match &s {
+ Some(x) => Some({
+ if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+ }),
+ None => None,
+ };
+
+ // Issue #7820
+ unsafe fn f(x: u32) -> u32 {
+ x
+ }
+ unsafe {
+ let _ = match Some(0) {
+ Some(x) => Some(f(x)),
+ None => None,
+ };
+ }
+ let _ = match Some(0) {
+ Some(x) => unsafe { Some(f(x)) },
+ None => None,
+ };
+ let _ = match Some(0) {
+ Some(x) => Some(unsafe { f(x) }),
+ None => None,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.stderr b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
new file mode 100644
index 000000000..d35b6252f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
@@ -0,0 +1,73 @@
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:8:13
+ |
+LL | let _ = match Some(0) {
+ | _____________^
+LL | | Some(x) => Some({
+LL | | let y = (String::new(), String::new());
+LL | | (x, y.0)
+LL | | }),
+LL | | None => None,
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::manual-map` implied by `-D warnings`
+help: try this
+ |
+LL ~ let _ = Some(0).map(|x| {
+LL + let y = (String::new(), String::new());
+LL + (x, y.0)
+LL ~ });
+ |
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:50:13
+ |
+LL | let _ = match &s {
+ | _____________^
+LL | | Some(x) => Some({
+LL | | if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+LL | | }),
+LL | | None => None,
+LL | | };
+ | |_____^
+ |
+help: try this
+ |
+LL ~ let _ = s.as_ref().map(|x| {
+LL + if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+LL ~ });
+ |
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:62:17
+ |
+LL | let _ = match Some(0) {
+ | _________________^
+LL | | Some(x) => Some(f(x)),
+LL | | None => None,
+LL | | };
+ | |_________^ help: try this: `Some(0).map(|x| f(x))`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:67:13
+ |
+LL | let _ = match Some(0) {
+ | _____________^
+LL | | Some(x) => unsafe { Some(f(x)) },
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:71:13
+ |
+LL | let _ = match Some(0) {
+ | _____________^
+LL | | Some(x) => Some(unsafe { f(x) }),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.rs
new file mode 100644
index 000000000..c826b082a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.rs
@@ -0,0 +1,88 @@
+#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+
+pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
+ let mut count = 0;
+ for i in 3..src.len() {
+ dst[i] = src[count];
+ count += 1;
+ }
+
+ let mut count = 0;
+ for i in 3..src.len() {
+ dst[count] = src[i];
+ count += 1;
+ }
+
+ let mut count = 3;
+ for i in 0..src.len() {
+ dst[count] = src[i];
+ count += 1;
+ }
+
+ let mut count = 3;
+ for i in 0..src.len() {
+ dst[i] = src[count];
+ count += 1;
+ }
+
+ let mut count = 0;
+ for i in 3..(3 + src.len()) {
+ dst[i] = src[count];
+ count += 1;
+ }
+
+ let mut count = 3;
+ for i in 5..src.len() {
+ dst[i] = src[count - 2];
+ count += 1;
+ }
+
+ let mut count = 2;
+ for i in 0..dst.len() {
+ dst[i] = src[count];
+ count += 1;
+ }
+
+ let mut count = 5;
+ for i in 3..10 {
+ dst[i] = src[count];
+ count += 1;
+ }
+
+ let mut count = 3;
+ let mut count2 = 30;
+ for i in 0..src.len() {
+ dst[count] = src[i];
+ dst2[count2] = src[i];
+ count += 1;
+ count2 += 1;
+ }
+
+ // make sure parentheses are added properly to bitwise operators, which have lower precedence than
+ // arithmetic ones
+ let mut count = 0 << 1;
+ for i in 0..1 << 1 {
+ dst[count] = src[i + 2];
+ count += 1;
+ }
+
+ // make sure incrementing expressions without semicolons at the end of loops are handled correctly.
+ let mut count = 0;
+ for i in 3..src.len() {
+ dst[i] = src[count];
+ count += 1
+ }
+
+ // make sure ones where the increment is not at the end of the loop.
+ // As a possible enhancement, one could adjust the offset in the suggestion according to
+ // the position. For example, if the increment is at the top of the loop;
+ // treating the loop counter as if it were initialized 1 greater than the original value.
+ let mut count = 0;
+ #[allow(clippy::needless_range_loop)]
+ for i in 0..src.len() {
+ count += 1;
+ dst[i] = src[count];
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr
new file mode 100644
index 000000000..79d40c0bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr
@@ -0,0 +1,111 @@
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:5:5
+ |
+LL | / for i in 3..src.len() {
+LL | | dst[i] = src[count];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);`
+ |
+ = note: `-D clippy::manual-memcpy` implied by `-D warnings`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:11:5
+ |
+LL | / for i in 3..src.len() {
+LL | | dst[count] = src[i];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:17:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[count] = src[i];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:23:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[i] = src[count];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:29:5
+ |
+LL | / for i in 3..(3 + src.len()) {
+LL | | dst[i] = src[count];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:35:5
+ |
+LL | / for i in 5..src.len() {
+LL | | dst[i] = src[count - 2];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:41:5
+ |
+LL | / for i in 0..dst.len() {
+LL | | dst[i] = src[count];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:47:5
+ |
+LL | / for i in 3..10 {
+LL | | dst[i] = src[count];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:54:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[count] = src[i];
+LL | | dst2[count2] = src[i];
+LL | | count += 1;
+LL | | count2 += 1;
+LL | | }
+ | |_____^
+ |
+help: try replacing the loop by
+ |
+LL ~ dst[3..(src.len() + 3)].copy_from_slice(&src[..]);
+LL + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]);
+ |
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:64:5
+ |
+LL | / for i in 0..1 << 1 {
+LL | | dst[count] = src[i + 2];
+LL | | count += 1;
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/with_loop_counters.rs:71:5
+ |
+LL | / for i in 3..src.len() {
+LL | | dst[i] = src[count];
+LL | | count += 1
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs
new file mode 100644
index 000000000..ea0535d07
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs
@@ -0,0 +1,136 @@
+#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+
+const LOOP_OFFSET: usize = 5000;
+
+pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
+ // plain manual memcpy
+ for i in 0..src.len() {
+ dst[i] = src[i];
+ }
+
+ // dst offset memcpy
+ for i in 0..src.len() {
+ dst[i + 10] = src[i];
+ }
+
+ // src offset memcpy
+ for i in 0..src.len() {
+ dst[i] = src[i + 10];
+ }
+
+ // src offset memcpy
+ for i in 11..src.len() {
+ dst[i] = src[i - 10];
+ }
+
+ // overwrite entire dst
+ for i in 0..dst.len() {
+ dst[i] = src[i];
+ }
+
+ // manual copy with branch - can't easily convert to memcpy!
+ for i in 0..src.len() {
+ dst[i] = src[i];
+ if dst[i] > 5 {
+ break;
+ }
+ }
+
+ // multiple copies - suggest two memcpy statements
+ for i in 10..256 {
+ dst[i] = src[i - 5];
+ dst2[i + 500] = src[i]
+ }
+
+ // this is a reversal - the copy lint shouldn't be triggered
+ for i in 10..LOOP_OFFSET {
+ dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i];
+ }
+
+ let some_var = 5;
+ // Offset in variable
+ for i in 10..LOOP_OFFSET {
+ dst[i + LOOP_OFFSET] = src[i - some_var];
+ }
+
+ // Non continuous copy - don't trigger lint
+ for i in 0..10 {
+ dst[i + i] = src[i];
+ }
+
+ let src_vec = vec![1, 2, 3, 4, 5];
+ let mut dst_vec = vec![0, 0, 0, 0, 0];
+
+ // make sure vectors are supported
+ for i in 0..src_vec.len() {
+ dst_vec[i] = src_vec[i];
+ }
+
+ // lint should not trigger when either
+ // source or destination type is not
+ // slice-like, like DummyStruct
+ struct DummyStruct(i32);
+
+ impl ::std::ops::Index<usize> for DummyStruct {
+ type Output = i32;
+
+ fn index(&self, _: usize) -> &i32 {
+ &self.0
+ }
+ }
+
+ let src = DummyStruct(5);
+ let mut dst_vec = vec![0; 10];
+
+ for i in 0..10 {
+ dst_vec[i] = src[i];
+ }
+
+ // Simplify suggestion (issue #3004)
+ let src = [0, 1, 2, 3, 4];
+ let mut dst = [0, 0, 0, 0, 0, 0];
+ let from = 1;
+
+ for i in from..from + src.len() {
+ dst[i] = src[i - from];
+ }
+
+ for i in from..from + 3 {
+ dst[i] = src[i - from];
+ }
+
+ #[allow(clippy::identity_op)]
+ for i in 0..5 {
+ dst[i - 0] = src[i];
+ }
+
+ #[allow(clippy::reversed_empty_ranges)]
+ for i in 0..0 {
+ dst[i] = src[i];
+ }
+
+ // `RangeTo` `for` loop - don't trigger lint
+ for i in 0.. {
+ dst[i] = src[i];
+ }
+
+ // VecDeque - ideally this would work, but would require something like `range_as_slices`
+ let mut dst = std::collections::VecDeque::from_iter([0; 5]);
+ let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]);
+ for i in 0..dst.len() {
+ dst[i] = src[i];
+ }
+ let src = vec![0, 1, 2, 3, 4];
+ for i in 0..dst.len() {
+ dst[i] = src[i];
+ }
+}
+
+#[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
+pub fn manual_clone(src: &[String], dst: &mut [String]) {
+ for i in 0..src.len() {
+ dst[i] = src[i].clone();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr
new file mode 100644
index 000000000..c163ae061
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr
@@ -0,0 +1,115 @@
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:7:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[i] = src[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[..]);`
+ |
+ = note: `-D clippy::manual-memcpy` implied by `-D warnings`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:12:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[i + 10] = src[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:17:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[i] = src[i + 10];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:22:5
+ |
+LL | / for i in 11..src.len() {
+LL | | dst[i] = src[i - 10];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:27:5
+ |
+LL | / for i in 0..dst.len() {
+LL | | dst[i] = src[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:40:5
+ |
+LL | / for i in 10..256 {
+LL | | dst[i] = src[i - 5];
+LL | | dst2[i + 500] = src[i]
+LL | | }
+ | |_____^
+ |
+help: try replacing the loop by
+ |
+LL ~ dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]);
+LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]);
+ |
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:52:5
+ |
+LL | / for i in 10..LOOP_OFFSET {
+LL | | dst[i + LOOP_OFFSET] = src[i - some_var];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:65:5
+ |
+LL | / for i in 0..src_vec.len() {
+LL | | dst_vec[i] = src_vec[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:94:5
+ |
+LL | / for i in from..from + src.len() {
+LL | | dst[i] = src[i - from];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:98:5
+ |
+LL | / for i in from..from + 3 {
+LL | | dst[i] = src[i - from];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:103:5
+ |
+LL | / for i in 0..5 {
+LL | | dst[i - 0] = src[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:108:5
+ |
+LL | / for i in 0..0 {
+LL | | dst[i] = src[i];
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);`
+
+error: it looks like you're manually copying between slices
+ --> $DIR/without_loop_counters.rs:131:5
+ |
+LL | / for i in 0..src.len() {
+LL | | dst[i] = src[i].clone();
+LL | | }
+ | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
new file mode 100644
index 000000000..03b2433f6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
@@ -0,0 +1,87 @@
+#![feature(lint_reasons)]
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+enum E {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// user forgot to remove the marker
+#[non_exhaustive]
+enum Ep {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// marker variant does not have doc hidden attribute, should be ignored
+enum NoDocHidden {
+ A,
+ B,
+ _C,
+}
+
+// name of variant with doc hidden does not start with underscore, should be ignored
+enum NoUnderscore {
+ A,
+ B,
+ #[doc(hidden)]
+ C,
+}
+
+// variant with doc hidden is not unit, should be ignored
+enum NotUnit {
+ A,
+ B,
+ #[doc(hidden)]
+ _C(bool),
+}
+
+// variant with doc hidden is the only one, should be ignored
+enum OnlyMarker {
+ #[doc(hidden)]
+ _A,
+}
+
+// variant with multiple markers, should be ignored
+enum MultipleMarkers {
+ A,
+ #[doc(hidden)]
+ _B,
+ #[doc(hidden)]
+ _C,
+}
+
+// already non_exhaustive and no markers, should be ignored
+#[non_exhaustive]
+enum NonExhaustive {
+ A,
+ B,
+}
+
+// marked is used, don't lint
+enum UsedHidden {
+ #[doc(hidden)]
+ _A,
+ B,
+ C,
+}
+fn foo(x: &mut UsedHidden) {
+ if matches!(*x, UsedHidden::B) {
+ *x = UsedHidden::_A;
+ }
+}
+
+#[expect(clippy::manual_non_exhaustive)]
+enum ExpectLint {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
new file mode 100644
index 000000000..144fe86df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
@@ -0,0 +1,41 @@
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:5:1
+ |
+LL | enum E {
+ | ^-----
+ | |
+ | _help: add the attribute: `#[non_exhaustive] enum E`
+ | |
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:9:5
+ |
+LL | _C,
+ | ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:14:1
+ |
+LL | / enum Ep {
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:18:5
+ |
+LL | _C,
+ | ^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
new file mode 100644
index 000000000..498eee444
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
@@ -0,0 +1,74 @@
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+mod structs {
+ struct S {
+ pub a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // user forgot to remove the private field
+ #[non_exhaustive]
+ struct Sp {
+ pub a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // some other fields are private, should be ignored
+ struct PrivateFields {
+ a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // private field name does not start with underscore, should be ignored
+ struct NoUnderscore {
+ pub a: i32,
+ pub b: i32,
+ c: (),
+ }
+
+ // private field is not unit type, should be ignored
+ struct NotUnit {
+ pub a: i32,
+ pub b: i32,
+ _c: i32,
+ }
+
+ // private field is the only field, should be ignored
+ struct OnlyMarker {
+ _a: (),
+ }
+
+ // already non exhaustive and no private fields, should be ignored
+ #[non_exhaustive]
+ struct NonExhaustive {
+ pub a: i32,
+ pub b: i32,
+ }
+}
+
+mod tuple_structs {
+ struct T(pub i32, pub i32, ());
+
+ // user forgot to remove the private field
+ #[non_exhaustive]
+ struct Tp(pub i32, pub i32, ());
+
+ // some other fields are private, should be ignored
+ struct PrivateFields(pub i32, i32, ());
+
+ // private field is not unit type, should be ignored
+ struct NotUnit(pub i32, pub i32, i32);
+
+ // private field is the only field, should be ignored
+ struct OnlyMarker(());
+
+ // already non exhaustive and no private fields, should be ignored
+ #[non_exhaustive]
+ struct NonExhaustive(pub i32, pub i32);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
new file mode 100644
index 000000000..e0766c17b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
@@ -0,0 +1,65 @@
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:5:5
+ |
+LL | struct S {
+ | ^-------
+ | |
+ | _____help: add the attribute: `#[non_exhaustive] struct S`
+ | |
+LL | | pub a: i32,
+LL | | pub b: i32,
+LL | | _c: (),
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:8:9
+ |
+LL | _c: (),
+ | ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:13:5
+ |
+LL | / struct Sp {
+LL | | pub a: i32,
+LL | | pub b: i32,
+LL | | _c: (),
+LL | | }
+ | |_____^
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:16:9
+ |
+LL | _c: (),
+ | ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:54:5
+ |
+LL | struct T(pub i32, pub i32, ());
+ | --------^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: add the attribute: `#[non_exhaustive] struct T`
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:54:32
+ |
+LL | struct T(pub i32, pub i32, ());
+ | ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:58:5
+ |
+LL | struct Tp(pub i32, pub i32, ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:58:33
+ |
+LL | struct Tp(pub i32, pub i32, ());
+ | ^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_ok_or.fixed b/src/tools/clippy/tests/ui/manual_ok_or.fixed
new file mode 100644
index 000000000..887a97d7a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ok_or.fixed
@@ -0,0 +1,40 @@
+// run-rustfix
+#![warn(clippy::manual_ok_or)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::redundant_closure)]
+#![allow(dead_code)]
+#![allow(unused_must_use)]
+
+fn main() {
+ // basic case
+ let foo: Option<i32> = None;
+ foo.ok_or("error");
+
+ // eta expansion case
+ foo.ok_or("error");
+
+ // turbo fish syntax
+ None::<i32>.ok_or("error");
+
+ // multiline case
+ #[rustfmt::skip]
+ foo.ok_or(&format!(
+ "{}{}{}{}{}{}{}",
+ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
+
+ // not applicable, closure isn't direct `Ok` wrapping
+ foo.map_or(Err("error"), |v| Ok(v + 1));
+
+ // not applicable, or side isn't `Result::Err`
+ foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
+
+ // not applicable, expr is not a `Result` value
+ foo.map_or(42, |v| v);
+
+ // TODO patterns not covered yet
+ match foo {
+ Some(v) => Ok(v),
+ None => Err("error"),
+ };
+ foo.map_or_else(|| Err("error"), |v| Ok(v));
+}
diff --git a/src/tools/clippy/tests/ui/manual_ok_or.rs b/src/tools/clippy/tests/ui/manual_ok_or.rs
new file mode 100644
index 000000000..3c99872f5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ok_or.rs
@@ -0,0 +1,44 @@
+// run-rustfix
+#![warn(clippy::manual_ok_or)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::redundant_closure)]
+#![allow(dead_code)]
+#![allow(unused_must_use)]
+
+fn main() {
+ // basic case
+ let foo: Option<i32> = None;
+ foo.map_or(Err("error"), |v| Ok(v));
+
+ // eta expansion case
+ foo.map_or(Err("error"), Ok);
+
+ // turbo fish syntax
+ None::<i32>.map_or(Err("error"), |v| Ok(v));
+
+ // multiline case
+ #[rustfmt::skip]
+ foo.map_or(Err::<i32, &str>(
+ &format!(
+ "{}{}{}{}{}{}{}",
+ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
+ ),
+ |v| Ok(v),
+ );
+
+ // not applicable, closure isn't direct `Ok` wrapping
+ foo.map_or(Err("error"), |v| Ok(v + 1));
+
+ // not applicable, or side isn't `Result::Err`
+ foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
+
+ // not applicable, expr is not a `Result` value
+ foo.map_or(42, |v| v);
+
+ // TODO patterns not covered yet
+ match foo {
+ Some(v) => Ok(v),
+ None => Err("error"),
+ };
+ foo.map_or_else(|| Err("error"), |v| Ok(v));
+}
diff --git a/src/tools/clippy/tests/ui/manual_ok_or.stderr b/src/tools/clippy/tests/ui/manual_ok_or.stderr
new file mode 100644
index 000000000..65459a097
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_ok_or.stderr
@@ -0,0 +1,41 @@
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:11:5
+ |
+LL | foo.map_or(Err("error"), |v| Ok(v));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
+ |
+ = note: `-D clippy::manual-ok-or` implied by `-D warnings`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:14:5
+ |
+LL | foo.map_or(Err("error"), Ok);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:17:5
+ |
+LL | None::<i32>.map_or(Err("error"), |v| Ok(v));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::<i32>.ok_or("error")`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:21:5
+ |
+LL | / foo.map_or(Err::<i32, &str>(
+LL | | &format!(
+LL | | "{}{}{}{}{}{}{}",
+LL | | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
+LL | | ),
+LL | | |v| Ok(v),
+LL | | );
+ | |_____^
+ |
+help: replace with
+ |
+LL ~ foo.ok_or(&format!(
+LL + "{}{}{}{}{}{}{}",
+LL ~ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
new file mode 100644
index 000000000..5601c96c1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -0,0 +1,55 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::manual_rem_euclid)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! internal_rem_euclid {
+ () => {
+ let value: i32 = 5;
+ let _: i32 = value.rem_euclid(4);
+ };
+}
+
+fn main() {
+ let value: i32 = 5;
+
+ let _: i32 = value.rem_euclid(4);
+ let _: i32 = value.rem_euclid(4);
+ let _: i32 = value.rem_euclid(4);
+ let _: i32 = value.rem_euclid(4);
+ let _: i32 = 1 + value.rem_euclid(4);
+
+ let _: i32 = (3 + value % 4) % 4;
+ let _: i32 = (-4 + value % -4) % -4;
+ let _: i32 = ((5 % 4) + 4) % 4;
+
+ // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
+ // integer type
+ let not_annotated = 24;
+ let _ = ((not_annotated % 4) + 4) % 4;
+ let inferred: _ = 24;
+ let _ = ((inferred % 4) + 4) % 4;
+
+ // For lint to apply the constant must always be on the RHS of the previous value for %
+ let _: i32 = 4 % ((value % 4) + 4);
+ let _: i32 = ((4 % value) + 4) % 4;
+
+ // Lint in internal macros
+ internal_rem_euclid!();
+
+ // Do not lint in external macros
+ manual_rem_euclid!();
+}
+
+// Should lint for params too
+pub fn rem_euclid_4(num: i32) -> i32 {
+ num.rem_euclid(4)
+}
+
+// Constant version came later, should still lint
+pub const fn const_rem_euclid_4(num: i32) -> i32 {
+ num.rem_euclid(4)
+}
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
new file mode 100644
index 000000000..52135be26
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::manual_rem_euclid)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! internal_rem_euclid {
+ () => {
+ let value: i32 = 5;
+ let _: i32 = ((value % 4) + 4) % 4;
+ };
+}
+
+fn main() {
+ let value: i32 = 5;
+
+ let _: i32 = ((value % 4) + 4) % 4;
+ let _: i32 = (4 + (value % 4)) % 4;
+ let _: i32 = (value % 4 + 4) % 4;
+ let _: i32 = (4 + value % 4) % 4;
+ let _: i32 = 1 + (4 + value % 4) % 4;
+
+ let _: i32 = (3 + value % 4) % 4;
+ let _: i32 = (-4 + value % -4) % -4;
+ let _: i32 = ((5 % 4) + 4) % 4;
+
+ // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
+ // integer type
+ let not_annotated = 24;
+ let _ = ((not_annotated % 4) + 4) % 4;
+ let inferred: _ = 24;
+ let _ = ((inferred % 4) + 4) % 4;
+
+ // For lint to apply the constant must always be on the RHS of the previous value for %
+ let _: i32 = 4 % ((value % 4) + 4);
+ let _: i32 = ((4 % value) + 4) % 4;
+
+ // Lint in internal macros
+ internal_rem_euclid!();
+
+ // Do not lint in external macros
+ manual_rem_euclid!();
+}
+
+// Should lint for params too
+pub fn rem_euclid_4(num: i32) -> i32 {
+ ((num % 4) + 4) % 4
+}
+
+// Constant version came later, should still lint
+pub const fn const_rem_euclid_4(num: i32) -> i32 {
+ ((num % 4) + 4) % 4
+}
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
new file mode 100644
index 000000000..a237fd021
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -0,0 +1,57 @@
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:19:18
+ |
+LL | let _: i32 = ((value % 4) + 4) % 4;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+ |
+ = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:20:18
+ |
+LL | let _: i32 = (4 + (value % 4)) % 4;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:21:18
+ |
+LL | let _: i32 = (value % 4 + 4) % 4;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:22:18
+ |
+LL | let _: i32 = (4 + value % 4) % 4;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:23:22
+ |
+LL | let _: i32 = 1 + (4 + value % 4) % 4;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:12:22
+ |
+LL | let _: i32 = ((value % 4) + 4) % 4;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+...
+LL | internal_rem_euclid!();
+ | ---------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:49:5
+ |
+LL | ((num % 4) + 4) % 4
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+ --> $DIR/manual_rem_euclid.rs:54:5
+ |
+LL | ((num % 4) + 4) % 4
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed
new file mode 100644
index 000000000..fba503a20
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.fixed
@@ -0,0 +1,240 @@
+// run-rustfix
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_retain)]
+#![allow(unused)]
+use std::collections::BTreeMap;
+use std::collections::BTreeSet;
+use std::collections::BinaryHeap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+ binary_heap_retain();
+ btree_set_retain();
+ btree_map_retain();
+ hash_set_retain();
+ hash_map_retain();
+ string_retain();
+ vec_deque_retain();
+ vec_retain();
+ _msrv_153();
+ _msrv_126();
+ _msrv_118();
+}
+
+fn binary_heap_retain() {
+ // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
+ // And we need to add a test case for msrv if we update this implmention.
+ // https://github.com/rust-lang/rust/issues/71503
+ let mut heap = BinaryHeap::from([1, 2, 3]);
+ heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
+ heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+ heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+ // Do not lint, because type conversion is performed
+ heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
+ heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
+ heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn btree_map_retain() {
+ let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ // Do lint.
+ btree_map.retain(|k, _| k % 2 == 0);
+ btree_map.retain(|_, &mut v| v % 2 == 0);
+ btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
+
+ // Do not lint.
+ btree_map = btree_map
+ .into_iter()
+ .filter(|(x, _)| x % 2 == 0)
+ .collect::<BTreeMap<i8, i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn btree_set_retain() {
+ let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+
+ // Do lint.
+ btree_set.retain(|x| x % 2 == 0);
+ btree_set.retain(|x| x % 2 == 0);
+ btree_set.retain(|x| x % 2 == 0);
+
+ // Do not lint, because type conversion is performed
+ btree_set = btree_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<BTreeSet<i8>>();
+
+ btree_set = btree_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<BTreeSet<i8>>();
+
+ btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn hash_map_retain() {
+ let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ // Do lint.
+ hash_map.retain(|k, _| k % 2 == 0);
+ hash_map.retain(|_, &mut v| v % 2 == 0);
+ hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
+
+ // Do not lint.
+ hash_map = hash_map
+ .into_iter()
+ .filter(|(x, _)| x % 2 == 0)
+ .collect::<HashMap<i8, i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn hash_set_retain() {
+ let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+ // Do lint.
+ hash_set.retain(|x| x % 2 == 0);
+ hash_set.retain(|x| x % 2 == 0);
+ hash_set.retain(|x| x % 2 == 0);
+
+ // Do not lint, because type conversion is performed
+ hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
+ hash_set = hash_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<HashSet<i8>>();
+
+ hash_set = hash_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<HashSet<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
+}
+
+fn string_retain() {
+ let mut s = String::from("foobar");
+ // Do lint.
+ s.retain(|c| c != 'o');
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn vec_retain() {
+ let mut vec = vec![0, 1, 2];
+ // Do lint.
+ vec.retain(|x| x % 2 == 0);
+ vec.retain(|x| x % 2 == 0);
+ vec.retain(|x| x % 2 == 0);
+
+ // Do not lint, because type conversion is performed
+ vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
+ vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
+ vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn vec_deque_retain() {
+ let mut vec_deque = VecDeque::new();
+ vec_deque.extend(1..5);
+
+ // Do lint.
+ vec_deque.retain(|x| x % 2 == 0);
+ vec_deque.retain(|x| x % 2 == 0);
+ vec_deque.retain(|x| x % 2 == 0);
+
+ // Do not lint, because type conversion is performed
+ vec_deque = vec_deque
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<VecDeque<i8>>();
+ vec_deque = vec_deque
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<VecDeque<i8>>();
+ vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn _msrv_153() {
+ #![clippy::msrv = "1.52"]
+ let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+ btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+}
+
+fn _msrv_126() {
+ #![clippy::msrv = "1.25"]
+ let mut s = String::from("foobar");
+ s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn _msrv_118() {
+ #![clippy::msrv = "1.17"]
+ let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+ hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+ let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs
new file mode 100644
index 000000000..81a849fe7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.rs
@@ -0,0 +1,246 @@
+// run-rustfix
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_retain)]
+#![allow(unused)]
+use std::collections::BTreeMap;
+use std::collections::BTreeSet;
+use std::collections::BinaryHeap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+ binary_heap_retain();
+ btree_set_retain();
+ btree_map_retain();
+ hash_set_retain();
+ hash_map_retain();
+ string_retain();
+ vec_deque_retain();
+ vec_retain();
+ _msrv_153();
+ _msrv_126();
+ _msrv_118();
+}
+
+fn binary_heap_retain() {
+ // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
+ // And we need to add a test case for msrv if we update this implmention.
+ // https://github.com/rust-lang/rust/issues/71503
+ let mut heap = BinaryHeap::from([1, 2, 3]);
+ heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
+ heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+ heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+ // Do not lint, because type conversion is performed
+ heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
+ heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
+ heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn btree_map_retain() {
+ let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ // Do lint.
+ btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+ btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+ btree_map = btree_map
+ .into_iter()
+ .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+ .collect();
+
+ // Do not lint.
+ btree_map = btree_map
+ .into_iter()
+ .filter(|(x, _)| x % 2 == 0)
+ .collect::<BTreeMap<i8, i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn btree_set_retain() {
+ let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+
+ // Do lint.
+ btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because type conversion is performed
+ btree_set = btree_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<BTreeSet<i8>>();
+
+ btree_set = btree_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<BTreeSet<i8>>();
+
+ btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn hash_map_retain() {
+ let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ // Do lint.
+ hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+ hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+ hash_map = hash_map
+ .into_iter()
+ .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+ .collect();
+
+ // Do not lint.
+ hash_map = hash_map
+ .into_iter()
+ .filter(|(x, _)| x % 2 == 0)
+ .collect::<HashMap<i8, i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn hash_set_retain() {
+ let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+ // Do lint.
+ hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+ hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+ // Do not lint, because type conversion is performed
+ hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
+ hash_set = hash_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<HashSet<i8>>();
+
+ hash_set = hash_set
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<HashSet<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
+}
+
+fn string_retain() {
+ let mut s = String::from("foobar");
+ // Do lint.
+ s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn vec_retain() {
+ let mut vec = vec![0, 1, 2];
+ // Do lint.
+ vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+ vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because type conversion is performed
+ vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
+ vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
+ vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn vec_deque_retain() {
+ let mut vec_deque = VecDeque::new();
+ vec_deque.extend(1..5);
+
+ // Do lint.
+ vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+ vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because type conversion is performed
+ vec_deque = vec_deque
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .copied()
+ .collect::<VecDeque<i8>>();
+ vec_deque = vec_deque
+ .iter()
+ .filter(|&x| x % 2 == 0)
+ .cloned()
+ .collect::<VecDeque<i8>>();
+ vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
+
+ // Do not lint, because this expression is not assign.
+ let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+ let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+ // Do not lint, because it is an assignment to a different variable.
+ bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+ bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn _msrv_153() {
+ #![clippy::msrv = "1.52"]
+ let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+ let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+ btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+}
+
+fn _msrv_126() {
+ #![clippy::msrv = "1.25"]
+ let mut s = String::from("foobar");
+ s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn _msrv_118() {
+ #![clippy::msrv = "1.17"]
+ let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+ hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+ let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+ hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr
new file mode 100644
index 000000000..ec635919b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.stderr
@@ -0,0 +1,124 @@
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:52:5
+ |
+LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)`
+ |
+ = note: `-D clippy::manual-retain` implied by `-D warnings`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:53:5
+ |
+LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:54:5
+ |
+LL | / btree_map = btree_map
+LL | | .into_iter()
+LL | | .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+LL | | .collect();
+ | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:76:5
+ |
+LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:77:5
+ |
+LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:78:5
+ |
+LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:108:5
+ |
+LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:109:5
+ |
+LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:110:5
+ |
+LL | / hash_map = hash_map
+LL | | .into_iter()
+LL | | .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+LL | | .collect();
+ | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:131:5
+ |
+LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:132:5
+ |
+LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:133:5
+ |
+LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:162:5
+ |
+LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:174:5
+ |
+LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:175:5
+ |
+LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:176:5
+ |
+LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:198:5
+ |
+LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:199:5
+ |
+LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+ --> $DIR/manual_retain.rs:200:5
+ |
+LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
new file mode 100644
index 000000000..c4f53c446
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+
+#![allow(unused_imports)]
+
+use std::{i128, i32, u128, u32};
+
+fn main() {
+ let _ = 1u32.saturating_add(1);
+ let _ = 1u32.saturating_add(1);
+ let _ = 1u8.saturating_add(1);
+ let _ = 1u128.saturating_add(1);
+ let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
+ let _ = 1u8.checked_add(1).unwrap_or(0); // ok
+ let _ = 1u32.saturating_mul(1);
+
+ let _ = 1u32.saturating_sub(1);
+ let _ = 1u32.saturating_sub(1);
+ let _ = 1u8.saturating_sub(1);
+ let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
+ let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
+
+ let _ = 1i32.saturating_add(1);
+ let _ = 1i32.saturating_add(1);
+ let _ = 1i8.saturating_add(1);
+ let _ = 1i128.saturating_add(1);
+ let _ = 1i32.saturating_add(-1);
+ let _ = 1i32.saturating_add(-1);
+ let _ = 1i8.saturating_add(-1);
+ let _ = 1i128.saturating_add(-1);
+ let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
+ let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
+ let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
+
+ let _ = 1i32.saturating_sub(1);
+ let _ = 1i32.saturating_sub(1);
+ let _ = 1i8.saturating_sub(1);
+ let _ = 1i128.saturating_sub(1);
+ let _ = 1i32.saturating_sub(-1);
+ let _ = 1i32.saturating_sub(-1);
+ let _ = 1i8.saturating_sub(-1);
+ let _ = 1i128.saturating_sub(-1);
+ let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
+ let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
+ let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
+}
diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
new file mode 100644
index 000000000..cd83cf6e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+
+#![allow(unused_imports)]
+
+use std::{i128, i32, u128, u32};
+
+fn main() {
+ let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
+ let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
+ let _ = 1u8.checked_add(1).unwrap_or(255);
+ let _ = 1u128
+ .checked_add(1)
+ .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
+ let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
+ let _ = 1u8.checked_add(1).unwrap_or(0); // ok
+ let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
+
+ let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
+ let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
+ let _ = 1u8.checked_sub(1).unwrap_or(0);
+ let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
+ let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
+
+ let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
+ let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
+ let _ = 1i8.checked_add(1).unwrap_or(127);
+ let _ = 1i128
+ .checked_add(1)
+ .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
+ let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
+ let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
+ let _ = 1i8.checked_add(-1).unwrap_or(-128);
+ let _ = 1i128
+ .checked_add(-1)
+ .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
+ let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
+ let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
+ let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
+
+ let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
+ let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
+ let _ = 1i8.checked_sub(1).unwrap_or(-128);
+ let _ = 1i128
+ .checked_sub(1)
+ .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
+ let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
+ let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
+ let _ = 1i8.checked_sub(-1).unwrap_or(127);
+ let _ = 1i128
+ .checked_sub(-1)
+ .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
+ let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
+ let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
+ let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
+}
diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr
new file mode 100644
index 000000000..d985f2e75
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr
@@ -0,0 +1,163 @@
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:8:13
+ |
+LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
+ |
+ = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:9:13
+ |
+LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:10:13
+ |
+LL | let _ = 1u8.checked_add(1).unwrap_or(255);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:11:13
+ |
+LL | let _ = 1u128
+ | _____________^
+LL | | .checked_add(1)
+LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
+ | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:16:13
+ |
+LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:18:13
+ |
+LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:19:13
+ |
+LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:20:13
+ |
+LL | let _ = 1u8.checked_sub(1).unwrap_or(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:24:13
+ |
+LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:25:13
+ |
+LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:26:13
+ |
+LL | let _ = 1i8.checked_add(1).unwrap_or(127);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:27:13
+ |
+LL | let _ = 1i128
+ | _____________^
+LL | | .checked_add(1)
+LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
+ | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:30:13
+ |
+LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:31:13
+ |
+LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:32:13
+ |
+LL | let _ = 1i8.checked_add(-1).unwrap_or(-128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:33:13
+ |
+LL | let _ = 1i128
+ | _____________^
+LL | | .checked_add(-1)
+LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
+ | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:40:13
+ |
+LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:41:13
+ |
+LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:42:13
+ |
+LL | let _ = 1i8.checked_sub(1).unwrap_or(-128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:43:13
+ |
+LL | let _ = 1i128
+ | _____________^
+LL | | .checked_sub(1)
+LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
+ | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:46:13
+ |
+LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:47:13
+ |
+LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:48:13
+ |
+LL | let _ = 1i8.checked_sub(-1).unwrap_or(127);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)`
+
+error: manual saturating arithmetic
+ --> $DIR/manual_saturating_arithmetic.rs:49:13
+ |
+LL | let _ = 1i128
+ | _____________^
+LL | | .checked_sub(-1)
+LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
+ | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)`
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed
new file mode 100644
index 000000000..c7ca77043
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.fixed
@@ -0,0 +1,147 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let _ = "key=value".splitn(2, '=').nth(2);
+ let _ = "key=value".split_once('=').unwrap().1;
+ let _ = "key=value".split_once('=').unwrap().1;
+ let (_, _) = "key=value".split_once('=').unwrap();
+
+ let s = String::from("key=value");
+ let _ = s.split_once('=').unwrap().1;
+
+ let s = Box::<str>::from("key=value");
+ let _ = s.split_once('=').unwrap().1;
+
+ let s = &"key=value";
+ let _ = s.split_once('=').unwrap().1;
+
+ fn _f(s: &str) -> Option<&str> {
+ let _ = s.split_once('=')?.1;
+ let _ = s.split_once('=')?.1;
+ let _ = s.rsplit_once('=')?.0;
+ let _ = s.rsplit_once('=')?.0;
+ None
+ }
+
+ // Don't lint, slices don't have `split_once`
+ let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+ // `rsplitn` gives the results in the reverse order of `rsplit_once`
+ let _ = "key=value".rsplit_once('=').unwrap().0;
+ let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
+ let _ = s.rsplit_once('=').map(|x| x.0);
+}
+
+fn indirect() -> Option<()> {
+ let (l, r) = "a.b.c".split_once('.').unwrap();
+
+
+
+ let (l, r) = "a.b.c".split_once('.')?;
+
+
+
+ let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+
+
+
+ let (l, r) = "a.b.c".rsplit_once('.')?;
+
+
+
+ // could lint, currently doesn't
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let other = 1;
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let mut mut_binding = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let tuple = (iter.next()?, iter.next()?);
+
+ // should not lint
+
+ let mut missing_unwrap = "a.b.c".splitn(2, '.');
+ let l = missing_unwrap.next();
+ let r = missing_unwrap.next();
+
+ let mut mixed_unrap = "a.b.c".splitn(2, '.');
+ let unwrap = mixed_unrap.next().unwrap();
+ let question_mark = mixed_unrap.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let same_name = iter.next()?;
+ let same_name = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let shadows_existing = "d";
+ let shadows_existing = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let becomes_shadowed = iter.next()?;
+ let becomes_shadowed = "d";
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+ let third_usage = iter.next()?;
+
+ let mut n_three = "a.b.c".splitn(3, '.');
+ let l = n_three.next()?;
+ let r = n_three.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ {
+ let in_block = iter.next()?;
+ }
+ let r = iter.next()?;
+
+ let mut lacks_binding = "a.b.c".splitn(2, '.');
+ let _ = lacks_binding.next()?;
+ let r = lacks_binding.next()?;
+
+ let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut assigned = "";
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ assigned = iter.next()?;
+
+ None
+}
+
+fn _msrv_1_51() {
+ #![clippy::msrv = "1.51"]
+ // `str::split_once` was stabilized in 1.52. Do not lint this
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
+}
+
+fn _msrv_1_52() {
+ #![clippy::msrv = "1.52"]
+ let _ = "key=value".split_once('=').unwrap().1;
+
+ let (a, b) = "a.b.c".split_once('.').unwrap();
+
+
+}
diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs
new file mode 100644
index 000000000..ee2848a25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.rs
@@ -0,0 +1,147 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let _ = "key=value".splitn(2, '=').nth(2);
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+ let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+
+ let s = String::from("key=value");
+ let _ = s.splitn(2, '=').nth(1).unwrap();
+
+ let s = Box::<str>::from("key=value");
+ let _ = s.splitn(2, '=').nth(1).unwrap();
+
+ let s = &"key=value";
+ let _ = s.splitn(2, '=').skip(1).next().unwrap();
+
+ fn _f(s: &str) -> Option<&str> {
+ let _ = s.splitn(2, '=').nth(1)?;
+ let _ = s.splitn(2, '=').skip(1).next()?;
+ let _ = s.rsplitn(2, '=').nth(1)?;
+ let _ = s.rsplitn(2, '=').skip(1).next()?;
+ None
+ }
+
+ // Don't lint, slices don't have `split_once`
+ let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+ // `rsplitn` gives the results in the reverse order of `rsplit_once`
+ let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+ let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ let _ = s.rsplitn(2, '=').nth(1);
+}
+
+fn indirect() -> Option<()> {
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next().unwrap();
+ let r = iter.next().unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".rsplitn(2, '.');
+ let r = iter.next().unwrap();
+ let l = iter.next().unwrap();
+
+ let mut iter = "a.b.c".rsplitn(2, '.');
+ let r = iter.next()?;
+ let l = iter.next()?;
+
+ // could lint, currently doesn't
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let other = 1;
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let mut mut_binding = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let tuple = (iter.next()?, iter.next()?);
+
+ // should not lint
+
+ let mut missing_unwrap = "a.b.c".splitn(2, '.');
+ let l = missing_unwrap.next();
+ let r = missing_unwrap.next();
+
+ let mut mixed_unrap = "a.b.c".splitn(2, '.');
+ let unwrap = mixed_unrap.next().unwrap();
+ let question_mark = mixed_unrap.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let same_name = iter.next()?;
+ let same_name = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let shadows_existing = "d";
+ let shadows_existing = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let becomes_shadowed = iter.next()?;
+ let becomes_shadowed = "d";
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+ let third_usage = iter.next()?;
+
+ let mut n_three = "a.b.c".splitn(3, '.');
+ let l = n_three.next()?;
+ let r = n_three.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ {
+ let in_block = iter.next()?;
+ }
+ let r = iter.next()?;
+
+ let mut lacks_binding = "a.b.c".splitn(2, '.');
+ let _ = lacks_binding.next()?;
+ let r = lacks_binding.next()?;
+
+ let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut assigned = "";
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ assigned = iter.next()?;
+
+ None
+}
+
+fn _msrv_1_51() {
+ #![clippy::msrv = "1.51"]
+ // `str::split_once` was stabilized in 1.52. Do not lint this
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
+}
+
+fn _msrv_1_52() {
+ #![clippy::msrv = "1.52"]
+ let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr
new file mode 100644
index 000000000..269669468
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.stderr
@@ -0,0 +1,213 @@
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:14:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+ |
+ = note: `-D clippy::manual-split-once` implied by `-D warnings`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:15:13
+ |
+LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:16:18
+ |
+LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:19:13
+ |
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:22:13
+ |
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:25:13
+ |
+LL | let _ = s.splitn(2, '=').skip(1).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:28:17
+ |
+LL | let _ = s.splitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:29:17
+ |
+LL | let _ = s.splitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:30:17
+ |
+LL | let _ = s.rsplitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:31:17
+ |
+LL | let _ = s.rsplitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:39:13
+ |
+LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:40:18
+ |
+LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:41:13
+ |
+LL | let _ = s.rsplitn(2, '=').nth(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:45:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let l = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let r = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (l, r) = "a.b.c".split_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next().unwrap();
+LL +
+ |
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:49:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let l = iter.next()?;
+ | --------------------- first usage here
+LL | let r = iter.next()?;
+ | --------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (l, r) = "a.b.c".split_once('.')?;
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next()?;
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next()?;
+LL +
+ |
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:53:5
+ |
+LL | let mut iter = "a.b.c".rsplitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let r = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let l = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `rsplit_once`
+ |
+LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next().unwrap();
+LL +
+ |
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:57:5
+ |
+LL | let mut iter = "a.b.c".rsplitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let r = iter.next()?;
+ | --------------------- first usage here
+LL | let l = iter.next()?;
+ | --------------------- second usage here
+ |
+help: try `rsplit_once`
+ |
+LL | let (l, r) = "a.b.c".rsplit_once('.')?;
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next()?;
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next()?;
+LL +
+ |
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:142:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:144:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let a = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let b = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (a, b) = "a.b.c".split_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let a = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let b = iter.next().unwrap();
+LL +
+ |
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.fixed b/src/tools/clippy/tests/ui/manual_str_repeat.fixed
new file mode 100644
index 000000000..0704ba2f9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.fixed
@@ -0,0 +1,66 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_str_repeat)]
+
+use std::borrow::Cow;
+use std::iter::repeat;
+
+fn main() {
+ let _: String = "test".repeat(10);
+ let _: String = "x".repeat(10);
+ let _: String = "'".repeat(10);
+ let _: String = "\"".repeat(10);
+
+ let x = "test";
+ let count = 10;
+ let _ = x.repeat(count + 2);
+
+ macro_rules! m {
+ ($e:expr) => {{ $e }};
+ }
+ // FIXME: macro args are fine
+ let _: String = repeat(m!("test")).take(m!(count)).collect();
+
+ let x = &x;
+ let _: String = (*x).repeat(count);
+
+ macro_rules! repeat_m {
+ ($e:expr) => {{ repeat($e) }};
+ }
+ // Don't lint, repeat is from a macro.
+ let _: String = repeat_m!("test").take(count).collect();
+
+ let x: Box<str> = Box::from("test");
+ let _: String = x.repeat(count);
+
+ #[derive(Clone)]
+ struct S;
+ impl FromIterator<Box<S>> for String {
+ fn from_iter<T: IntoIterator<Item = Box<S>>>(_: T) -> Self {
+ Self::new()
+ }
+ }
+ // Don't lint, wrong box type
+ let _: String = repeat(Box::new(S)).take(count).collect();
+
+ let _: String = Cow::Borrowed("test").repeat(count);
+
+ let x = "x".to_owned();
+ let _: String = x.repeat(count);
+
+ let x = 'x';
+ // Don't lint, not char literal
+ let _: String = repeat(x).take(count).collect();
+}
+
+fn _msrv_1_15() {
+ #![clippy::msrv = "1.15"]
+ // `str::repeat` was stabilized in 1.16. Do not lint this
+ let _: String = std::iter::repeat("test").take(10).collect();
+}
+
+fn _msrv_1_16() {
+ #![clippy::msrv = "1.16"]
+ let _: String = "test".repeat(10);
+}
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.rs b/src/tools/clippy/tests/ui/manual_str_repeat.rs
new file mode 100644
index 000000000..f522be439
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.rs
@@ -0,0 +1,66 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_str_repeat)]
+
+use std::borrow::Cow;
+use std::iter::repeat;
+
+fn main() {
+ let _: String = std::iter::repeat("test").take(10).collect();
+ let _: String = std::iter::repeat('x').take(10).collect();
+ let _: String = std::iter::repeat('\'').take(10).collect();
+ let _: String = std::iter::repeat('"').take(10).collect();
+
+ let x = "test";
+ let count = 10;
+ let _ = repeat(x).take(count + 2).collect::<String>();
+
+ macro_rules! m {
+ ($e:expr) => {{ $e }};
+ }
+ // FIXME: macro args are fine
+ let _: String = repeat(m!("test")).take(m!(count)).collect();
+
+ let x = &x;
+ let _: String = repeat(*x).take(count).collect();
+
+ macro_rules! repeat_m {
+ ($e:expr) => {{ repeat($e) }};
+ }
+ // Don't lint, repeat is from a macro.
+ let _: String = repeat_m!("test").take(count).collect();
+
+ let x: Box<str> = Box::from("test");
+ let _: String = repeat(x).take(count).collect();
+
+ #[derive(Clone)]
+ struct S;
+ impl FromIterator<Box<S>> for String {
+ fn from_iter<T: IntoIterator<Item = Box<S>>>(_: T) -> Self {
+ Self::new()
+ }
+ }
+ // Don't lint, wrong box type
+ let _: String = repeat(Box::new(S)).take(count).collect();
+
+ let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
+
+ let x = "x".to_owned();
+ let _: String = repeat(x).take(count).collect();
+
+ let x = 'x';
+ // Don't lint, not char literal
+ let _: String = repeat(x).take(count).collect();
+}
+
+fn _msrv_1_15() {
+ #![clippy::msrv = "1.15"]
+ // `str::repeat` was stabilized in 1.16. Do not lint this
+ let _: String = std::iter::repeat("test").take(10).collect();
+}
+
+fn _msrv_1_16() {
+ #![clippy::msrv = "1.16"]
+ let _: String = std::iter::repeat("test").take(10).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.stderr b/src/tools/clippy/tests/ui/manual_str_repeat.stderr
new file mode 100644
index 000000000..c65116897
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_str_repeat.stderr
@@ -0,0 +1,64 @@
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:10:21
+ |
+LL | let _: String = std::iter::repeat("test").take(10).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
+ |
+ = note: `-D clippy::manual-str-repeat` implied by `-D warnings`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:11:21
+ |
+LL | let _: String = std::iter::repeat('x').take(10).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:12:21
+ |
+LL | let _: String = std::iter::repeat('/'').take(10).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:13:21
+ |
+LL | let _: String = std::iter::repeat('"').take(10).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:17:13
+ |
+LL | let _ = repeat(x).take(count + 2).collect::<String>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:26:21
+ |
+LL | let _: String = repeat(*x).take(count).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:35:21
+ |
+LL | let _: String = repeat(x).take(count).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:47:21
+ |
+LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:50:21
+ |
+LL | let _: String = repeat(x).take(count).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
+
+error: manual implementation of `str::repeat` using iterators
+ --> $DIR/manual_str_repeat.rs:65:21
+ |
+LL | let _: String = std::iter::repeat("test").take(10).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_strip.rs b/src/tools/clippy/tests/ui/manual_strip.rs
new file mode 100644
index 000000000..cbb84eb5c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_strip.rs
@@ -0,0 +1,66 @@
+#![warn(clippy::manual_strip)]
+
+fn main() {
+ let s = "abc";
+
+ if s.starts_with("ab") {
+ str::to_string(&s["ab".len()..]);
+ s["ab".len()..].to_string();
+
+ str::to_string(&s[2..]);
+ s[2..].to_string();
+ }
+
+ if s.ends_with("bc") {
+ str::to_string(&s[..s.len() - "bc".len()]);
+ s[..s.len() - "bc".len()].to_string();
+
+ str::to_string(&s[..s.len() - 2]);
+ s[..s.len() - 2].to_string();
+ }
+
+ // Character patterns
+ if s.starts_with('a') {
+ str::to_string(&s[1..]);
+ s[1..].to_string();
+ }
+
+ // Variable prefix
+ let prefix = "ab";
+ if s.starts_with(prefix) {
+ str::to_string(&s[prefix.len()..]);
+ }
+
+ // Constant prefix
+ const PREFIX: &str = "ab";
+ if s.starts_with(PREFIX) {
+ str::to_string(&s[PREFIX.len()..]);
+ str::to_string(&s[2..]);
+ }
+
+ // Constant target
+ const TARGET: &str = "abc";
+ if TARGET.starts_with(prefix) {
+ str::to_string(&TARGET[prefix.len()..]);
+ }
+
+ // String target - not mutated.
+ let s1: String = "abc".into();
+ if s1.starts_with("ab") {
+ s1[2..].to_uppercase();
+ }
+
+ // String target - mutated. (Don't lint.)
+ let mut s2: String = "abc".into();
+ if s2.starts_with("ab") {
+ s2.push('d');
+ s2[2..].to_uppercase();
+ }
+
+ // Target not stripped. (Don't lint.)
+ let s3 = String::from("abcd");
+ let s4 = String::from("efgh");
+ if s3.starts_with("ab") {
+ s4[2..].to_string();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/manual_strip.stderr b/src/tools/clippy/tests/ui/manual_strip.stderr
new file mode 100644
index 000000000..896edf2ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_strip.stderr
@@ -0,0 +1,132 @@
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:7:24
+ |
+LL | str::to_string(&s["ab".len()..]);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-strip` implied by `-D warnings`
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:6:5
+ |
+LL | if s.starts_with("ab") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix("ab") {
+LL ~ str::to_string(<stripped>);
+LL ~ <stripped>.to_string();
+LL |
+LL ~ str::to_string(<stripped>);
+LL ~ <stripped>.to_string();
+ |
+
+error: stripping a suffix manually
+ --> $DIR/manual_strip.rs:15:24
+ |
+LL | str::to_string(&s[..s.len() - "bc".len()]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the suffix was tested here
+ --> $DIR/manual_strip.rs:14:5
+ |
+LL | if s.ends_with("bc") {
+ | ^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_suffix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_suffix("bc") {
+LL ~ str::to_string(<stripped>);
+LL ~ <stripped>.to_string();
+LL |
+LL ~ str::to_string(<stripped>);
+LL ~ <stripped>.to_string();
+ |
+
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:24:24
+ |
+LL | str::to_string(&s[1..]);
+ | ^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:23:5
+ |
+LL | if s.starts_with('a') {
+ | ^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix('a') {
+LL ~ str::to_string(<stripped>);
+LL ~ <stripped>.to_string();
+ |
+
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:31:24
+ |
+LL | str::to_string(&s[prefix.len()..]);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:30:5
+ |
+LL | if s.starts_with(prefix) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix(prefix) {
+LL ~ str::to_string(<stripped>);
+ |
+
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:37:24
+ |
+LL | str::to_string(&s[PREFIX.len()..]);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:36:5
+ |
+LL | if s.starts_with(PREFIX) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix(PREFIX) {
+LL ~ str::to_string(<stripped>);
+LL ~ str::to_string(<stripped>);
+ |
+
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:44:24
+ |
+LL | str::to_string(&TARGET[prefix.len()..]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:43:5
+ |
+LL | if TARGET.starts_with(prefix) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = TARGET.strip_prefix(prefix) {
+LL ~ str::to_string(<stripped>);
+ |
+
+error: stripping a prefix manually
+ --> $DIR/manual_strip.rs:50:9
+ |
+LL | s1[2..].to_uppercase();
+ | ^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/manual_strip.rs:49:5
+ |
+LL | if s1.starts_with("ab") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s1.strip_prefix("ab") {
+LL ~ <stripped>.to_uppercase();
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
new file mode 100644
index 000000000..7d6897821
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
@@ -0,0 +1,181 @@
+// run-rustfix
+#![allow(dead_code)]
+#![allow(unused_variables, clippy::unnecessary_wraps)]
+
+fn option_unwrap_or() {
+ // int case
+ Some(1).unwrap_or(42);
+
+ // int case reversed
+ Some(1).unwrap_or(42);
+
+ // richer none expr
+ Some(1).unwrap_or(1 + 42);
+
+ // multiline case
+ #[rustfmt::skip]
+ Some(1).unwrap_or({
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ });
+
+ // string case
+ Some("Bob").unwrap_or("Alice");
+
+ // don't lint
+ match Some(1) {
+ Some(i) => i + 2,
+ None => 42,
+ };
+ match Some(1) {
+ Some(i) => i,
+ None => return,
+ };
+ for j in 0..4 {
+ match Some(j) {
+ Some(i) => i,
+ None => continue,
+ };
+ match Some(j) {
+ Some(i) => i,
+ None => break,
+ };
+ }
+
+ // cases where the none arm isn't a constant expression
+ // are not linted due to potential ownership issues
+
+ // ownership issue example, don't lint
+ struct NonCopyable;
+ let mut option: Option<NonCopyable> = None;
+ match option {
+ Some(x) => x,
+ None => {
+ option = Some(NonCopyable);
+ // some more code ...
+ option.unwrap()
+ },
+ };
+
+ // ownership issue example, don't lint
+ let option: Option<&str> = None;
+ match option {
+ Some(s) => s,
+ None => &format!("{} {}!", "hello", "world"),
+ };
+}
+
+fn result_unwrap_or() {
+ // int case
+ Ok::<i32, &str>(1).unwrap_or(42);
+
+ // int case, scrutinee is a binding
+ let a = Ok::<i32, &str>(1);
+ a.unwrap_or(42);
+
+ // int case, suggestion must surround Result expr with parentheses
+ (Ok(1) as Result<i32, &str>).unwrap_or(42);
+
+ // method call case, suggestion must not surround Result expr `s.method()` with parentheses
+ struct S;
+ impl S {
+ fn method(self) -> Option<i32> {
+ Some(42)
+ }
+ }
+ let s = S {};
+ s.method().unwrap_or(42);
+
+ // int case reversed
+ Ok::<i32, &str>(1).unwrap_or(42);
+
+ // richer none expr
+ Ok::<i32, &str>(1).unwrap_or(1 + 42);
+
+ // multiline case
+ #[rustfmt::skip]
+ Ok::<i32, &str>(1).unwrap_or({
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ });
+
+ // string case
+ Ok::<&str, &str>("Bob").unwrap_or("Alice");
+
+ // don't lint
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i + 2,
+ Err(_) => 42,
+ };
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => return,
+ };
+ for j in 0..4 {
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => continue,
+ };
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => break,
+ };
+ }
+
+ // don't lint, Err value is used
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => s,
+ };
+ // could lint, but unused_variables takes care of it
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => "Bob",
+ };
+}
+
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+ match Some(1) {
+ Some(s) => s,
+ None => 0,
+ };
+}
+
+const fn const_fn_result_unwrap_or() {
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(_) => "Bob",
+ };
+}
+
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = some_macro!().unwrap_or(0);
+ }
+}
+
+use std::rc::Rc;
+fn format_name(name: Option<&Rc<str>>) -> &str {
+ match name {
+ None => "<anon>",
+ Some(name) => name,
+ }
+}
+
+fn implicit_deref_ref() {
+ let _: &str = match Some(&"bye") {
+ None => "hi",
+ Some(s) => s,
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
new file mode 100644
index 000000000..b937fe6f9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
@@ -0,0 +1,223 @@
+// run-rustfix
+#![allow(dead_code)]
+#![allow(unused_variables, clippy::unnecessary_wraps)]
+
+fn option_unwrap_or() {
+ // int case
+ match Some(1) {
+ Some(i) => i,
+ None => 42,
+ };
+
+ // int case reversed
+ match Some(1) {
+ None => 42,
+ Some(i) => i,
+ };
+
+ // richer none expr
+ match Some(1) {
+ Some(i) => i,
+ None => 1 + 42,
+ };
+
+ // multiline case
+ #[rustfmt::skip]
+ match Some(1) {
+ Some(i) => i,
+ None => {
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ }
+ };
+
+ // string case
+ match Some("Bob") {
+ Some(i) => i,
+ None => "Alice",
+ };
+
+ // don't lint
+ match Some(1) {
+ Some(i) => i + 2,
+ None => 42,
+ };
+ match Some(1) {
+ Some(i) => i,
+ None => return,
+ };
+ for j in 0..4 {
+ match Some(j) {
+ Some(i) => i,
+ None => continue,
+ };
+ match Some(j) {
+ Some(i) => i,
+ None => break,
+ };
+ }
+
+ // cases where the none arm isn't a constant expression
+ // are not linted due to potential ownership issues
+
+ // ownership issue example, don't lint
+ struct NonCopyable;
+ let mut option: Option<NonCopyable> = None;
+ match option {
+ Some(x) => x,
+ None => {
+ option = Some(NonCopyable);
+ // some more code ...
+ option.unwrap()
+ },
+ };
+
+ // ownership issue example, don't lint
+ let option: Option<&str> = None;
+ match option {
+ Some(s) => s,
+ None => &format!("{} {}!", "hello", "world"),
+ };
+}
+
+fn result_unwrap_or() {
+ // int case
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // int case, scrutinee is a binding
+ let a = Ok::<i32, &str>(1);
+ match a {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // int case, suggestion must surround Result expr with parentheses
+ match Ok(1) as Result<i32, &str> {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // method call case, suggestion must not surround Result expr `s.method()` with parentheses
+ struct S;
+ impl S {
+ fn method(self) -> Option<i32> {
+ Some(42)
+ }
+ }
+ let s = S {};
+ match s.method() {
+ Some(i) => i,
+ None => 42,
+ };
+
+ // int case reversed
+ match Ok::<i32, &str>(1) {
+ Err(_) => 42,
+ Ok(i) => i,
+ };
+
+ // richer none expr
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => 1 + 42,
+ };
+
+ // multiline case
+ #[rustfmt::skip]
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => {
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ }
+ };
+
+ // string case
+ match Ok::<&str, &str>("Bob") {
+ Ok(i) => i,
+ Err(_) => "Alice",
+ };
+
+ // don't lint
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i + 2,
+ Err(_) => 42,
+ };
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => return,
+ };
+ for j in 0..4 {
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => continue,
+ };
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => break,
+ };
+ }
+
+ // don't lint, Err value is used
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => s,
+ };
+ // could lint, but unused_variables takes care of it
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => "Bob",
+ };
+}
+
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+ match Some(1) {
+ Some(s) => s,
+ None => 0,
+ };
+}
+
+const fn const_fn_result_unwrap_or() {
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(_) => "Bob",
+ };
+}
+
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = match some_macro!() {
+ Some(val) => val,
+ None => 0,
+ };
+ }
+}
+
+use std::rc::Rc;
+fn format_name(name: Option<&Rc<str>>) -> &str {
+ match name {
+ None => "<anon>",
+ Some(name) => name,
+ }
+}
+
+fn implicit_deref_ref() {
+ let _: &str = match Some(&"bye") {
+ None => "hi",
+ Some(s) => s,
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr
new file mode 100644
index 000000000..0e4cb798d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr
@@ -0,0 +1,155 @@
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:7:5
+ |
+LL | / match Some(1) {
+LL | | Some(i) => i,
+LL | | None => 42,
+LL | | };
+ | |_____^ help: replace with: `Some(1).unwrap_or(42)`
+ |
+ = note: `-D clippy::manual-unwrap-or` implied by `-D warnings`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:13:5
+ |
+LL | / match Some(1) {
+LL | | None => 42,
+LL | | Some(i) => i,
+LL | | };
+ | |_____^ help: replace with: `Some(1).unwrap_or(42)`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:19:5
+ |
+LL | / match Some(1) {
+LL | | Some(i) => i,
+LL | | None => 1 + 42,
+LL | | };
+ | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:26:5
+ |
+LL | / match Some(1) {
+LL | | Some(i) => i,
+LL | | None => {
+LL | | 42 + 42
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+help: replace with
+ |
+LL ~ Some(1).unwrap_or({
+LL + 42 + 42
+LL + + 42 + 42 + 42
+LL + + 42 + 42 + 42
+LL ~ });
+ |
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:36:5
+ |
+LL | / match Some("Bob") {
+LL | | Some(i) => i,
+LL | | None => "Alice",
+LL | | };
+ | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:86:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:93:5
+ |
+LL | / match a {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `a.unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:99:5
+ |
+LL | / match Ok(1) as Result<i32, &str> {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `(Ok(1) as Result<i32, &str>).unwrap_or(42)`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:112:5
+ |
+LL | / match s.method() {
+LL | | Some(i) => i,
+LL | | None => 42,
+LL | | };
+ | |_____^ help: replace with: `s.method().unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:118:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Err(_) => 42,
+LL | | Ok(i) => i,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:124:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => 1 + 42,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(1 + 42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:131:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => {
+LL | | 42 + 42
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+help: replace with
+ |
+LL ~ Ok::<i32, &str>(1).unwrap_or({
+LL + 42 + 42
+LL + + 42 + 42 + 42
+LL + + 42 + 42 + 42
+LL ~ });
+ |
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:141:5
+ |
+LL | / match Ok::<&str, &str>("Bob") {
+LL | | Ok(i) => i,
+LL | | Err(_) => "Alice",
+LL | | };
+ | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:201:17
+ |
+LL | let _ = match some_macro!() {
+ | _________________^
+LL | | Some(val) => val,
+LL | | None => 0,
+LL | | };
+ | |_________^ help: replace with: `some_macro!().unwrap_or(0)`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/many_single_char_names.rs b/src/tools/clippy/tests/ui/many_single_char_names.rs
new file mode 100644
index 000000000..88fcce668
--- /dev/null
+++ b/src/tools/clippy/tests/ui/many_single_char_names.rs
@@ -0,0 +1,74 @@
+#![allow(clippy::too_many_arguments, clippy::diverging_sub_expression)]
+#![warn(clippy::many_single_char_names)]
+
+fn bla() {
+ let a: i32;
+ let (b, c, d): (i32, i64, i16);
+ {
+ {
+ let cdefg: i32;
+ let blar: i32;
+ }
+ {
+ let e: i32;
+ }
+ {
+ let e: i32;
+ let f: i32;
+ }
+ match 5 {
+ 1 => println!(),
+ e => panic!(),
+ }
+ match 5 {
+ 1 => println!(),
+ _ => panic!(),
+ }
+ }
+}
+
+fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
+
+fn bindings2() {
+ let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
+}
+
+fn shadowing() {
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ let a = 0i32;
+ {
+ let a = 0i32;
+ }
+}
+
+fn patterns() {
+ enum Z {
+ A(i32),
+ B(i32),
+ C(i32),
+ D(i32),
+ E(i32),
+ F(i32),
+ }
+
+ // These should not trigger a warning, since the pattern bindings are a new scope.
+ match Z::A(0) {
+ Z::A(a) => {},
+ Z::B(b) => {},
+ Z::C(c) => {},
+ Z::D(d) => {},
+ Z::E(e) => {},
+ Z::F(f) => {},
+ }
+}
+
+#[allow(clippy::many_single_char_names)]
+fn issue_3198_allow_works() {
+ let (a, b, c, d, e) = (0, 0, 0, 0, 0);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/many_single_char_names.stderr b/src/tools/clippy/tests/ui/many_single_char_names.stderr
new file mode 100644
index 000000000..ade0f84bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/many_single_char_names.stderr
@@ -0,0 +1,51 @@
+error: 5 bindings with single-character names in scope
+ --> $DIR/many_single_char_names.rs:5:9
+ |
+LL | let a: i32;
+ | ^
+LL | let (b, c, d): (i32, i64, i16);
+ | ^ ^ ^
+...
+LL | let e: i32;
+ | ^
+ |
+ = note: `-D clippy::many-single-char-names` implied by `-D warnings`
+
+error: 6 bindings with single-character names in scope
+ --> $DIR/many_single_char_names.rs:5:9
+ |
+LL | let a: i32;
+ | ^
+LL | let (b, c, d): (i32, i64, i16);
+ | ^ ^ ^
+...
+LL | let e: i32;
+ | ^
+LL | let f: i32;
+ | ^
+
+error: 5 bindings with single-character names in scope
+ --> $DIR/many_single_char_names.rs:5:9
+ |
+LL | let a: i32;
+ | ^
+LL | let (b, c, d): (i32, i64, i16);
+ | ^ ^ ^
+...
+LL | e => panic!(),
+ | ^
+
+error: 8 bindings with single-character names in scope
+ --> $DIR/many_single_char_names.rs:30:13
+ |
+LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
+ | ^ ^ ^ ^ ^ ^ ^ ^
+
+error: 8 bindings with single-character names in scope
+ --> $DIR/many_single_char_names.rs:33:10
+ |
+LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
+ | ^ ^ ^ ^ ^ ^ ^ ^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed
new file mode 100644
index 000000000..0860dcf8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_clone.fixed
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::map_clone)]
+#![allow(
+ clippy::clone_on_copy,
+ clippy::iter_cloned_collect,
+ clippy::many_single_char_names,
+ clippy::redundant_clone
+)]
+
+fn main() {
+ let _: Vec<i8> = vec![5_i8; 6].iter().copied().collect();
+ let _: Vec<String> = vec![String::new()].iter().cloned().collect();
+ let _: Vec<u32> = vec![42, 43].iter().copied().collect();
+ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
+ let _: Option<u64> = Some(&16).copied();
+ let _: Option<u8> = Some(&1).copied();
+
+ // Don't lint these
+ let v = vec![5_i8; 6];
+ let a = 0;
+ let b = &a;
+ let _ = v.iter().map(|_x| *b);
+ let _ = v.iter().map(|_x| a.clone());
+ let _ = v.iter().map(|&_x| a);
+
+ // Issue #498
+ let _ = std::env::args();
+
+ // Issue #4824 item types that aren't references
+ {
+ use std::rc::Rc;
+
+ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
+ let _: Option<u32> = o.map(|x| *x);
+ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ }
+
+ // Issue #5524 mutable references
+ {
+ let mut c = 42;
+ let v = vec![&mut c];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ let mut d = 21;
+ let v = vec![&mut d];
+ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
+ }
+
+ // Issue #6299
+ {
+ let mut aa = 5;
+ let mut bb = 3;
+ let items = vec![&mut aa, &mut bb];
+ let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect();
+ }
+
+ // Issue #6239 deref coercion and clone deref
+ {
+ use std::cell::RefCell;
+
+ let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs
new file mode 100644
index 000000000..b69873368
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_clone.rs
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::map_clone)]
+#![allow(
+ clippy::clone_on_copy,
+ clippy::iter_cloned_collect,
+ clippy::many_single_char_names,
+ clippy::redundant_clone
+)]
+
+fn main() {
+ let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
+ let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
+ let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
+ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
+ let _: Option<u64> = Some(&16).map(|b| *b);
+ let _: Option<u8> = Some(&1).map(|x| x.clone());
+
+ // Don't lint these
+ let v = vec![5_i8; 6];
+ let a = 0;
+ let b = &a;
+ let _ = v.iter().map(|_x| *b);
+ let _ = v.iter().map(|_x| a.clone());
+ let _ = v.iter().map(|&_x| a);
+
+ // Issue #498
+ let _ = std::env::args().map(|v| v.clone());
+
+ // Issue #4824 item types that aren't references
+ {
+ use std::rc::Rc;
+
+ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
+ let _: Option<u32> = o.map(|x| *x);
+ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ }
+
+ // Issue #5524 mutable references
+ {
+ let mut c = 42;
+ let v = vec![&mut c];
+ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
+ let mut d = 21;
+ let v = vec![&mut d];
+ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
+ }
+
+ // Issue #6299
+ {
+ let mut aa = 5;
+ let mut bb = 3;
+ let items = vec![&mut aa, &mut bb];
+ let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect();
+ }
+
+ // Issue #6239 deref coercion and clone deref
+ {
+ use std::cell::RefCell;
+
+ let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr
new file mode 100644
index 000000000..d84a5bf8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_clone.stderr
@@ -0,0 +1,40 @@
+error: you are using an explicit closure for copying elements
+ --> $DIR/map_clone.rs:11:22
+ |
+LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
+ |
+ = note: `-D clippy::map-clone` implied by `-D warnings`
+
+error: you are using an explicit closure for cloning elements
+ --> $DIR/map_clone.rs:12:26
+ |
+LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
+
+error: you are using an explicit closure for copying elements
+ --> $DIR/map_clone.rs:13:23
+ |
+LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
+
+error: you are using an explicit closure for copying elements
+ --> $DIR/map_clone.rs:15:26
+ |
+LL | let _: Option<u64> = Some(&16).map(|b| *b);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()`
+
+error: you are using an explicit closure for copying elements
+ --> $DIR/map_clone.rs:16:25
+ |
+LL | let _: Option<u8> = Some(&1).map(|x| x.clone());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()`
+
+error: you are needlessly cloning iterator elements
+ --> $DIR/map_clone.rs:27:29
+ |
+LL | let _ = std::env::args().map(|v| v.clone());
+ | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_collect_result_unit.fixed b/src/tools/clippy/tests/ui/map_collect_result_unit.fixed
new file mode 100644
index 000000000..e66c9cc24
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_collect_result_unit.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::map_collect_result_unit)]
+
+fn main() {
+ {
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ let _: Result<(), _> = (0..3).try_for_each(|t| Err(t + 1));
+
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ }
+}
+
+fn _ignore() {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<Vec<i32>, _>>();
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Vec<Result<(), _>>>();
+}
diff --git a/src/tools/clippy/tests/ui/map_collect_result_unit.rs b/src/tools/clippy/tests/ui/map_collect_result_unit.rs
new file mode 100644
index 000000000..6f08f4c3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_collect_result_unit.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::map_collect_result_unit)]
+
+fn main() {
+ {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<(), _>>();
+ let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect();
+
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ }
+}
+
+fn _ignore() {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<Vec<i32>, _>>();
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Vec<Result<(), _>>>();
+}
diff --git a/src/tools/clippy/tests/ui/map_collect_result_unit.stderr b/src/tools/clippy/tests/ui/map_collect_result_unit.stderr
new file mode 100644
index 000000000..8b06e13ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_collect_result_unit.stderr
@@ -0,0 +1,16 @@
+error: `.map().collect()` can be replaced with `.try_for_each()`
+ --> $DIR/map_collect_result_unit.rs:6:17
+ |
+LL | let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<(), _>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))`
+ |
+ = note: `-D clippy::map-collect-result-unit` implied by `-D warnings`
+
+error: `.map().collect()` can be replaced with `.try_for_each()`
+ --> $DIR/map_collect_result_unit.rs:7:32
+ |
+LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_err.rs b/src/tools/clippy/tests/ui/map_err.rs
new file mode 100644
index 000000000..bb35ab1a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_err.rs
@@ -0,0 +1,29 @@
+#![warn(clippy::map_err_ignore)]
+#![allow(clippy::unnecessary_wraps)]
+use std::error::Error;
+use std::fmt;
+
+#[derive(Debug)]
+enum Errors {
+ Ignored,
+}
+
+impl Error for Errors {}
+
+impl fmt::Display for Errors {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Error")
+ }
+}
+
+fn main() -> Result<(), Errors> {
+ let x = u32::try_from(-123_i32);
+
+ println!("{:?}", x.map_err(|_| Errors::Ignored));
+
+ // Should not warn you because you explicitly ignore the parameter
+ // using a named wildcard value
+ println!("{:?}", x.map_err(|_foo| Errors::Ignored));
+
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/map_err.stderr b/src/tools/clippy/tests/ui/map_err.stderr
new file mode 100644
index 000000000..c03584052
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_err.stderr
@@ -0,0 +1,11 @@
+error: `map_err(|_|...` wildcard pattern discards the original error
+ --> $DIR/map_err.rs:22:32
+ |
+LL | println!("{:?}", x.map_err(|_| Errors::Ignored));
+ | ^^^
+ |
+ = note: `-D clippy::map-err-ignore` implied by `-D warnings`
+ = help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs
new file mode 100644
index 000000000..7d47ee09d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten.rs
@@ -0,0 +1,55 @@
+#![warn(clippy::map_flatten)]
+#![feature(result_flattening)]
+
+// issue #8506, multi-line
+#[rustfmt::skip]
+fn long_span() {
+ let _: Option<i32> = Some(1)
+ .map(|x| {
+ if x <= 5 {
+ Some(x)
+ } else {
+ None
+ }
+ })
+ .flatten();
+
+ let _: Result<i32, i32> = Ok(1)
+ .map(|x| {
+ if x == 1 {
+ Ok(x)
+ } else {
+ Err(0)
+ }
+ })
+ .flatten();
+
+ let result: Result<i32, i32> = Ok(2);
+ fn do_something() { }
+ let _: Result<i32, i32> = result
+ .map(|res| {
+ if res > 0 {
+ do_something();
+ Ok(res)
+ } else {
+ Err(0)
+ }
+ })
+ .flatten();
+
+ let _: Vec<_> = vec![5_i8; 6]
+ .into_iter()
+ .map(|some_value| {
+ if some_value > 3 {
+ Some(some_value)
+ } else {
+ None
+ }
+ })
+ .flatten()
+ .collect();
+}
+
+fn main() {
+ long_span();
+}
diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr
new file mode 100644
index 000000000..4b2630d68
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten.stderr
@@ -0,0 +1,100 @@
+error: called `map(..).flatten()` on `Option`
+ --> $DIR/map_flatten.rs:8:10
+ |
+LL | .map(|x| {
+ | __________^
+LL | | if x <= 5 {
+LL | | Some(x)
+LL | | } else {
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+ = note: `-D clippy::map-flatten` implied by `-D warnings`
+help: try replacing `map` with `and_then` and remove the `.flatten()`
+ |
+LL ~ .and_then(|x| {
+LL + if x <= 5 {
+LL + Some(x)
+LL + } else {
+LL + None
+LL + }
+LL ~ });
+ |
+
+error: called `map(..).flatten()` on `Result`
+ --> $DIR/map_flatten.rs:18:10
+ |
+LL | .map(|x| {
+ | __________^
+LL | | if x == 1 {
+LL | | Ok(x)
+LL | | } else {
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+help: try replacing `map` with `and_then` and remove the `.flatten()`
+ |
+LL ~ .and_then(|x| {
+LL + if x == 1 {
+LL + Ok(x)
+LL + } else {
+LL + Err(0)
+LL + }
+LL ~ });
+ |
+
+error: called `map(..).flatten()` on `Result`
+ --> $DIR/map_flatten.rs:30:10
+ |
+LL | .map(|res| {
+ | __________^
+LL | | if res > 0 {
+LL | | do_something();
+LL | | Ok(res)
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+help: try replacing `map` with `and_then` and remove the `.flatten()`
+ |
+LL ~ .and_then(|res| {
+LL + if res > 0 {
+LL + do_something();
+LL + Ok(res)
+LL + } else {
+LL + Err(0)
+LL + }
+LL ~ });
+ |
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten.rs:42:10
+ |
+LL | .map(|some_value| {
+ | __________^
+LL | | if some_value > 3 {
+LL | | Some(some_value)
+LL | | } else {
+... |
+LL | | })
+LL | | .flatten()
+ | |__________________^
+ |
+help: try replacing `map` with `filter_map` and remove the `.flatten()`
+ |
+LL ~ .filter_map(|some_value| {
+LL + if some_value > 3 {
+LL + Some(some_value)
+LL + } else {
+LL + None
+LL + }
+LL + })
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed
new file mode 100644
index 000000000..312819a0a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed
@@ -0,0 +1,65 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::let_underscore_drop)]
+#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::unnecessary_wraps)]
+#![feature(result_flattening)]
+
+fn main() {
+ // mapping to Option on Iterator
+ fn option_id(x: i8) -> Option<i8> {
+ Some(x)
+ }
+ let option_id_ref: fn(i8) -> Option<i8> = option_id;
+ let option_id_closure = |x| Some(x);
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
+
+ // mapping to Iterator on Iterator
+ let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
+
+ // mapping to Option on Option
+ let _: Option<_> = (Some(Some(1))).and_then(|x| x);
+
+ // mapping to Result on Result
+ let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
+
+ issue8734();
+ issue8878();
+}
+
+fn issue8734() {
+ let _ = [0u8, 1, 2, 3]
+ .into_iter()
+ .flat_map(|n| match n {
+ 1 => [n
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)],
+ n => [n],
+ });
+}
+
+#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
+#[rustfmt::skip] // whitespace is important for this one
+fn issue8878() {
+ std::collections::HashMap::<u32, u32>::new()
+ .get(&0)
+ .and_then(|_| {
+// we need some newlines
+// so that the span is big enough
+// for a split output of the diagnostic
+ Some("")
+ // whitespace beforehand is important as well
+ });
+}
diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs
new file mode 100644
index 000000000..3fbf4f9a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(clippy::let_underscore_drop)]
+#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::unnecessary_wraps)]
+#![feature(result_flattening)]
+
+fn main() {
+ // mapping to Option on Iterator
+ fn option_id(x: i8) -> Option<i8> {
+ Some(x)
+ }
+ let option_id_ref: fn(i8) -> Option<i8> = option_id;
+ let option_id_closure = |x| Some(x);
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+
+ // mapping to Iterator on Iterator
+ let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+
+ // mapping to Option on Option
+ let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+
+ // mapping to Result on Result
+ let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+
+ issue8734();
+ issue8878();
+}
+
+fn issue8734() {
+ let _ = [0u8, 1, 2, 3]
+ .into_iter()
+ .map(|n| match n {
+ 1 => [n
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)],
+ n => [n],
+ })
+ .flatten();
+}
+
+#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
+#[rustfmt::skip] // whitespace is important for this one
+fn issue8878() {
+ std::collections::HashMap::<u32, u32>::new()
+ .get(&0)
+ .map(|_| {
+// we need some newlines
+// so that the span is big enough
+// for a split output of the diagnostic
+ Some("")
+ // whitespace beforehand is important as well
+ })
+ .flatten();
+}
diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr
new file mode 100644
index 000000000..c91f0b9ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr
@@ -0,0 +1,99 @@
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:18:47
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)`
+ |
+ = note: `-D clippy::map-flatten` implied by `-D warnings`
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:19:47
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)`
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:20:47
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)`
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:21:47
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))`
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:24:47
+ |
+LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)`
+
+error: called `map(..).flatten()` on `Option`
+ --> $DIR/map_flatten_fixable.rs:27:40
+ |
+LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
+ | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
+
+error: called `map(..).flatten()` on `Result`
+ --> $DIR/map_flatten_fixable.rs:30:42
+ |
+LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
+ | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
+
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:39:10
+ |
+LL | .map(|n| match n {
+ | __________^
+LL | | 1 => [n
+LL | | .saturating_add(1)
+LL | | .saturating_add(1)
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+help: try replacing `map` with `flat_map` and remove the `.flatten()`
+ |
+LL ~ .flat_map(|n| match n {
+LL + 1 => [n
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)],
+LL + n => [n],
+LL ~ });
+ |
+
+error: called `map(..).flatten()` on `Option`
+ --> $DIR/map_flatten_fixable.rs:59:10
+ |
+LL | .map(|_| {
+ | __________^
+LL | | // we need some newlines
+LL | | // so that the span is big enough
+LL | | // for a split output of the diagnostic
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+help: try replacing `map` with `and_then` and remove the `.flatten()`
+ |
+LL ~ .and_then(|_| {
+LL + // we need some newlines
+LL + // so that the span is big enough
+LL + // for a split output of the diagnostic
+LL + Some("")
+LL + // whitespace beforehand is important as well
+LL ~ });
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed
new file mode 100644
index 000000000..2256e51f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::map_identity)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+ let x: [u16; 3] = [1, 2, 3];
+ // should lint
+ let _: Vec<_> = x.iter().map(not_identity).collect();
+ let _: Vec<_> = x.iter().collect();
+ let _: Option<u8> = Some(3);
+ let _: Result<i8, f32> = Ok(-3);
+ // should not lint
+ let _: Vec<_> = x.iter().map(|x| 2 * x).collect();
+ let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect();
+ let _: Option<u8> = None.map(|x: u8| x - 1);
+ let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
+ return x + 3;
+ });
+ let _: Result<u32, u32> = Ok(1);
+ let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
+}
+
+fn not_identity(x: &u16) -> u16 {
+ *x
+}
diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs
new file mode 100644
index 000000000..ccfdc9ea7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+#![warn(clippy::map_identity)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+ let x: [u16; 3] = [1, 2, 3];
+ // should lint
+ let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
+ let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+ let _: Option<u8> = Some(3).map(|x| x);
+ let _: Result<i8, f32> = Ok(-3).map(|x| {
+ return x;
+ });
+ // should not lint
+ let _: Vec<_> = x.iter().map(|x| 2 * x).collect();
+ let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect();
+ let _: Option<u8> = None.map(|x: u8| x - 1);
+ let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
+ return x + 3;
+ });
+ let _: Result<u32, u32> = Ok(1).map_err(|a| a);
+ let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
+}
+
+fn not_identity(x: &u16) -> u16 {
+ *x
+}
diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr
new file mode 100644
index 000000000..b6a77281f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.stderr
@@ -0,0 +1,43 @@
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:8:47
+ |
+LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
+ | ^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+ |
+ = note: `-D clippy::map-identity` implied by `-D warnings`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:9:57
+ |
+LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+ | ^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:9:29
+ |
+LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:10:32
+ |
+LL | let _: Option<u8> = Some(3).map(|x| x);
+ | ^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:11:36
+ |
+LL | let _: Result<i8, f32> = Ok(-3).map(|x| {
+ | ____________________________________^
+LL | | return x;
+LL | | });
+ | |______^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:21:36
+ |
+LL | let _: Result<u32, u32> = Ok(1).map_err(|a| a);
+ | ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_unit_fn.rs b/src/tools/clippy/tests/ui/map_unit_fn.rs
new file mode 100644
index 000000000..e7f07b50f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unit_fn.rs
@@ -0,0 +1,11 @@
+#![allow(unused)]
+struct Mappable;
+
+impl Mappable {
+ pub fn map(&self) {}
+}
+
+fn main() {
+ let m = Mappable {};
+ m.map();
+}
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs
new file mode 100644
index 000000000..87e16f5d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs
@@ -0,0 +1,81 @@
+// aux-build:option_helpers.rs
+
+#![warn(clippy::map_unwrap_or)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::HashMap;
+
+#[rustfmt::skip]
+fn option_methods() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or(_)` use.
+ // Single line case.
+ let _ = opt.map(|x| x + 1)
+ // Should lint even though this call is on a separate line.
+ .unwrap_or(0);
+ // Multi-line cases.
+ let _ = opt.map(|x| {
+ x + 1
+ }
+ ).unwrap_or(0);
+ let _ = opt.map(|x| x + 1)
+ .unwrap_or({
+ 0
+ });
+ // Single line `map(f).unwrap_or(None)` case.
+ let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
+ // Multi-line `map(f).unwrap_or(None)` cases.
+ let _ = opt.map(|x| {
+ Some(x + 1)
+ }
+ ).unwrap_or(None);
+ let _ = opt
+ .map(|x| Some(x + 1))
+ .unwrap_or(None);
+ // macro case
+ let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
+
+ // Should not lint if not copyable
+ let id: String = "identifier".to_string();
+ let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
+ // ...but DO lint if the `unwrap_or` argument is not used in the `map`
+ let id: String = "identifier".to_string();
+ let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
+
+ // Check for `option.map(_).unwrap_or_else(_)` use.
+ // Multi-line cases.
+ let _ = opt.map(|x| {
+ x + 1
+ }
+ ).unwrap_or_else(|| 0);
+ let _ = opt.map(|x| x + 1)
+ .unwrap_or_else(||
+ 0
+ );
+}
+
+#[rustfmt::skip]
+fn result_methods() {
+ let res: Result<i32, ()> = Ok(1);
+
+ // Check for `result.map(_).unwrap_or_else(_)` use.
+ // multi line cases
+ let _ = res.map(|x| {
+ x + 1
+ }
+ ).unwrap_or_else(|_e| 0);
+ let _ = res.map(|x| x + 1)
+ .unwrap_or_else(|_e| {
+ 0
+ });
+ // macro case
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
+}
+
+fn main() {
+ option_methods();
+ result_methods();
+}
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
new file mode 100644
index 000000000..abc9c1ece
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr
@@ -0,0 +1,150 @@
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:16:13
+ |
+LL | let _ = opt.map(|x| x + 1)
+ | _____________^
+LL | | // Should lint even though this call is on a separate line.
+LL | | .unwrap_or(0);
+ | |_____________________^
+ |
+ = note: `-D clippy::map-unwrap-or` implied by `-D warnings`
+help: use `map_or(<a>, <f>)` instead
+ |
+LL - let _ = opt.map(|x| x + 1)
+LL + let _ = opt.map_or(0, |x| x + 1);
+ |
+
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:20:13
+ |
+LL | let _ = opt.map(|x| {
+ | _____________^
+LL | | x + 1
+LL | | }
+LL | | ).unwrap_or(0);
+ | |__________________^
+ |
+help: use `map_or(<a>, <f>)` instead
+ |
+LL ~ let _ = opt.map_or(0, |x| {
+LL | x + 1
+LL | }
+LL ~ );
+ |
+
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:24:13
+ |
+LL | let _ = opt.map(|x| x + 1)
+ | _____________^
+LL | | .unwrap_or({
+LL | | 0
+LL | | });
+ | |__________^
+ |
+help: use `map_or(<a>, <f>)` instead
+ |
+LL ~ let _ = opt.map_or({
+LL + 0
+LL ~ }, |x| x + 1);
+ |
+
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:29:13
+ |
+LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `and_then(<f>)` instead
+ |
+LL - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
+LL + let _ = opt.and_then(|x| Some(x + 1));
+ |
+
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:31:13
+ |
+LL | let _ = opt.map(|x| {
+ | _____________^
+LL | | Some(x + 1)
+LL | | }
+LL | | ).unwrap_or(None);
+ | |_____________________^
+ |
+help: use `and_then(<f>)` instead
+ |
+LL ~ let _ = opt.and_then(|x| {
+LL | Some(x + 1)
+LL | }
+LL ~ );
+ |
+
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:35:13
+ |
+LL | let _ = opt
+ | _____________^
+LL | | .map(|x| Some(x + 1))
+LL | | .unwrap_or(None);
+ | |________________________^
+ |
+help: use `and_then(<f>)` instead
+ |
+LL - .map(|x| Some(x + 1))
+LL + .and_then(|x| Some(x + 1));
+ |
+
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:46:13
+ |
+LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `map_or(<a>, <f>)` instead
+ |
+LL - let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
+LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
+ |
+
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:50:13
+ |
+LL | let _ = opt.map(|x| {
+ | _____________^
+LL | | x + 1
+LL | | }
+LL | | ).unwrap_or_else(|| 0);
+ | |__________________________^
+
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:54:13
+ |
+LL | let _ = opt.map(|x| x + 1)
+ | _____________^
+LL | | .unwrap_or_else(||
+LL | | 0
+LL | | );
+ | |_________^
+
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:66:13
+ |
+LL | let _ = res.map(|x| {
+ | _____________^
+LL | | x + 1
+LL | | }
+LL | | ).unwrap_or_else(|_e| 0);
+ | |____________________________^
+
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:70:13
+ |
+LL | let _ = res.map(|x| x + 1)
+ | _____________^
+LL | | .unwrap_or_else(|_e| {
+LL | | 0
+LL | | });
+ | |__________^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed
new file mode 100644
index 000000000..bd5b4f716
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed
@@ -0,0 +1,54 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::map_unwrap_or)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::HashMap;
+
+#[rustfmt::skip]
+fn option_methods() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = opt.map_or_else(|| 0, |x| x + 1);
+
+ // Macro case.
+ // Should not lint.
+ let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
+
+ // Issue #4144
+ {
+ let mut frequencies = HashMap::new();
+ let word = "foo";
+
+ frequencies
+ .get_mut(word)
+ .map(|count| {
+ *count += 1;
+ })
+ .unwrap_or_else(|| {
+ frequencies.insert(word.to_owned(), 1);
+ });
+ }
+}
+
+#[rustfmt::skip]
+fn result_methods() {
+ let res: Result<i32, ()> = Ok(1);
+
+ // Check for `result.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = res.map_or_else(|_e| 0, |x| x + 1);
+
+ // macro case
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
+}
+
+fn main() {
+ option_methods();
+ result_methods();
+}
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs
new file mode 100644
index 000000000..0b892caf2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::map_unwrap_or)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::HashMap;
+
+#[rustfmt::skip]
+fn option_methods() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = opt.map(|x| x + 1)
+ // Should lint even though this call is on a separate line.
+ .unwrap_or_else(|| 0);
+
+ // Macro case.
+ // Should not lint.
+ let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
+
+ // Issue #4144
+ {
+ let mut frequencies = HashMap::new();
+ let word = "foo";
+
+ frequencies
+ .get_mut(word)
+ .map(|count| {
+ *count += 1;
+ })
+ .unwrap_or_else(|| {
+ frequencies.insert(word.to_owned(), 1);
+ });
+ }
+}
+
+#[rustfmt::skip]
+fn result_methods() {
+ let res: Result<i32, ()> = Ok(1);
+
+ // Check for `result.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = res.map(|x| x + 1)
+ // should lint even though this call is on a separate line
+ .unwrap_or_else(|_e| 0);
+
+ // macro case
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
+}
+
+fn main() {
+ option_methods();
+ result_methods();
+}
diff --git a/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr
new file mode 100644
index 000000000..1837bc2ca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr
@@ -0,0 +1,22 @@
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or_fixable.rs:17:13
+ |
+LL | let _ = opt.map(|x| x + 1)
+ | _____________^
+LL | | // Should lint even though this call is on a separate line.
+LL | | .unwrap_or_else(|| 0);
+ | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)`
+ |
+ = note: `-D clippy::map-unwrap-or` implied by `-D warnings`
+
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or_fixable.rs:47:13
+ |
+LL | let _ = res.map(|x| x + 1)
+ | _____________^
+LL | | // should lint even though this call is on a separate line
+LL | | .unwrap_or_else(|_e| 0);
+ | |_______________________________^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed
new file mode 100644
index 000000000..ddfa1e741
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_as_ref.fixed
@@ -0,0 +1,43 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::match_as_ref)]
+
+fn match_as_ref() {
+ let owned: Option<()> = None;
+ let borrowed: Option<&()> = owned.as_ref();
+
+ let mut mut_owned: Option<()> = None;
+ let borrow_mut: Option<&mut ()> = mut_owned.as_mut();
+}
+
+mod issue4437 {
+ use std::{error::Error, fmt, num::ParseIntError};
+
+ #[derive(Debug)]
+ struct E {
+ source: Option<ParseIntError>,
+ }
+
+ impl Error for E {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.source.as_ref().map(|x| x as _)
+ }
+ }
+
+ impl fmt::Display for E {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unimplemented!()
+ }
+ }
+}
+
+fn main() {
+ // Don't lint
+ let _ = match Some(0) {
+ #[cfg(feature = "foo")]
+ Some(ref x) if *x > 50 => None,
+ Some(ref x) => Some(x),
+ None => None,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs
new file mode 100644
index 000000000..025d475ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_as_ref.rs
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::match_as_ref)]
+
+fn match_as_ref() {
+ let owned: Option<()> = None;
+ let borrowed: Option<&()> = match owned {
+ None => None,
+ Some(ref v) => Some(v),
+ };
+
+ let mut mut_owned: Option<()> = None;
+ let borrow_mut: Option<&mut ()> = match mut_owned {
+ None => None,
+ Some(ref mut v) => Some(v),
+ };
+}
+
+mod issue4437 {
+ use std::{error::Error, fmt, num::ParseIntError};
+
+ #[derive(Debug)]
+ struct E {
+ source: Option<ParseIntError>,
+ }
+
+ impl Error for E {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self.source {
+ Some(ref s) => Some(s),
+ None => None,
+ }
+ }
+ }
+
+ impl fmt::Display for E {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unimplemented!()
+ }
+ }
+}
+
+fn main() {
+ // Don't lint
+ let _ = match Some(0) {
+ #[cfg(feature = "foo")]
+ Some(ref x) if *x > 50 => None,
+ Some(ref x) => Some(x),
+ None => None,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/match_as_ref.stderr b/src/tools/clippy/tests/ui/match_as_ref.stderr
new file mode 100644
index 000000000..c3b62849c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_as_ref.stderr
@@ -0,0 +1,33 @@
+error: use `as_ref()` instead
+ --> $DIR/match_as_ref.rs:8:33
+ |
+LL | let borrowed: Option<&()> = match owned {
+ | _________________________________^
+LL | | None => None,
+LL | | Some(ref v) => Some(v),
+LL | | };
+ | |_____^ help: try this: `owned.as_ref()`
+ |
+ = note: `-D clippy::match-as-ref` implied by `-D warnings`
+
+error: use `as_mut()` instead
+ --> $DIR/match_as_ref.rs:14:39
+ |
+LL | let borrow_mut: Option<&mut ()> = match mut_owned {
+ | _______________________________________^
+LL | | None => None,
+LL | | Some(ref mut v) => Some(v),
+LL | | };
+ | |_____^ help: try this: `mut_owned.as_mut()`
+
+error: use `as_ref()` instead
+ --> $DIR/match_as_ref.rs:30:13
+ |
+LL | / match self.source {
+LL | | Some(ref s) => Some(s),
+LL | | None => None,
+LL | | }
+ | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs
new file mode 100644
index 000000000..bcc999a49
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_bool.rs
@@ -0,0 +1,63 @@
+#![deny(clippy::match_bool)]
+
+fn match_bool() {
+ let test: bool = true;
+
+ match test {
+ true => 0,
+ false => 42,
+ };
+
+ let option = 1;
+ match option == 1 {
+ true => 1,
+ false => 0,
+ };
+
+ match test {
+ true => (),
+ false => {
+ println!("Noooo!");
+ },
+ };
+
+ match test {
+ false => {
+ println!("Noooo!");
+ },
+ _ => (),
+ };
+
+ match test && test {
+ false => {
+ println!("Noooo!");
+ },
+ _ => (),
+ };
+
+ match test {
+ false => {
+ println!("Noooo!");
+ },
+ true => {
+ println!("Yes!");
+ },
+ };
+
+ // Not linted
+ match option {
+ 1..=10 => 1,
+ 11..=20 => 2,
+ _ => 3,
+ };
+
+ // Don't lint
+ let _ = match test {
+ #[cfg(feature = "foo")]
+ true if option == 5 => 10,
+ true => 0,
+ false => 1,
+ };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr
new file mode 100644
index 000000000..3fd0468e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_bool.stderr
@@ -0,0 +1,117 @@
+error: this boolean expression can be simplified
+ --> $DIR/match_bool.rs:31:11
+ |
+LL | match test && test {
+ | ^^^^^^^^^^^^ help: try: `test`
+ |
+ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:6:5
+ |
+LL | / match test {
+LL | | true => 0,
+LL | | false => 42,
+LL | | };
+ | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }`
+ |
+note: the lint level is defined here
+ --> $DIR/match_bool.rs:1:9
+ |
+LL | #![deny(clippy::match_bool)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:12:5
+ |
+LL | / match option == 1 {
+LL | | true => 1,
+LL | | false => 0,
+LL | | };
+ | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }`
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:17:5
+ |
+LL | / match test {
+LL | | true => (),
+LL | | false => {
+LL | | println!("Noooo!");
+LL | | },
+LL | | };
+ | |_____^
+ |
+help: consider using an `if`/`else` expression
+ |
+LL ~ if !test {
+LL + println!("Noooo!");
+LL ~ };
+ |
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:24:5
+ |
+LL | / match test {
+LL | | false => {
+LL | | println!("Noooo!");
+LL | | },
+LL | | _ => (),
+LL | | };
+ | |_____^
+ |
+help: consider using an `if`/`else` expression
+ |
+LL ~ if !test {
+LL + println!("Noooo!");
+LL ~ };
+ |
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:31:5
+ |
+LL | / match test && test {
+LL | | false => {
+LL | | println!("Noooo!");
+LL | | },
+LL | | _ => (),
+LL | | };
+ | |_____^
+ |
+help: consider using an `if`/`else` expression
+ |
+LL ~ if !(test && test) {
+LL + println!("Noooo!");
+LL ~ };
+ |
+
+error: equal expressions as operands to `&&`
+ --> $DIR/match_bool.rs:31:11
+ |
+LL | match test && test {
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[deny(clippy::eq_op)]` on by default
+
+error: you seem to be trying to match on a boolean expression
+ --> $DIR/match_bool.rs:38:5
+ |
+LL | / match test {
+LL | | false => {
+LL | | println!("Noooo!");
+LL | | },
+... |
+LL | | },
+LL | | };
+ | |_____^
+ |
+help: consider using an `if`/`else` expression
+ |
+LL ~ if test {
+LL + println!("Yes!");
+LL + } else {
+LL + println!("Noooo!");
+LL ~ };
+ |
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
new file mode 100644
index 000000000..1ccbfda64
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
@@ -0,0 +1,170 @@
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
+#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
+
+fn main() {
+ let x = Some(5);
+
+ // Lint
+ let _y = matches!(x, Some(0));
+
+ // Lint
+ let _w = matches!(x, Some(_));
+
+ // Turn into is_none
+ let _z = x.is_none();
+
+ // Lint
+ let _zz = !matches!(x, Some(r) if r == 0);
+
+ // Lint
+ let _zzz = matches!(x, Some(5));
+
+ // No lint
+ let _a = match x {
+ Some(_) => false,
+ _ => false,
+ };
+
+ // No lint
+ let _ab = match x {
+ Some(0) => false,
+ _ => true,
+ None => false,
+ };
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ }
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
+ {
+ // lint
+ // skip rustfmt to prevent removing block for first pattern
+ #[rustfmt::skip]
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
+ {
+ // lint
+ let _ans = !matches!(x, E::B(_) | E::C);
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // should print "z" in suggestion (#6503)
+ let z = &Some(3);
+ let _z = matches!(z, Some(3));
+ }
+
+ {
+ // this could also print "z" in suggestion..?
+ let z = Some(3);
+ let _z = matches!(&z, Some(3));
+ }
+
+ {
+ enum AnEnum {
+ X,
+ Y,
+ }
+
+ fn foo(_x: AnEnum) {}
+
+ fn main() {
+ let z = AnEnum::X;
+ // we can't remove the reference here!
+ let _ = matches!(&z, AnEnum::X);
+ foo(z);
+ }
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ // we need the reference here because later val is consumed by fun()
+ let _res = matches!(&val, &Some(ref _a));
+ fun(val);
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ let _res = matches!(&val, &Some(ref _a));
+ fun(val);
+ }
+
+ {
+ enum E {
+ A,
+ B,
+ C,
+ }
+
+ let _ = match E::A {
+ E::B => true,
+ #[cfg(feature = "foo")]
+ E::A => true,
+ _ => false,
+ };
+ }
+}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
new file mode 100644
index 000000000..a49991f59
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
@@ -0,0 +1,211 @@
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
+#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
+
+fn main() {
+ let x = Some(5);
+
+ // Lint
+ let _y = match x {
+ Some(0) => true,
+ _ => false,
+ };
+
+ // Lint
+ let _w = match x {
+ Some(_) => true,
+ _ => false,
+ };
+
+ // Turn into is_none
+ let _z = match x {
+ Some(_) => false,
+ None => true,
+ };
+
+ // Lint
+ let _zz = match x {
+ Some(r) if r == 0 => false,
+ _ => true,
+ };
+
+ // Lint
+ let _zzz = if let Some(5) = x { true } else { false };
+
+ // No lint
+ let _a = match x {
+ Some(_) => false,
+ _ => false,
+ };
+
+ // No lint
+ let _ab = match x {
+ Some(0) => false,
+ _ => true,
+ None => false,
+ };
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ }
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+ {
+ // lint
+ // skip rustfmt to prevent removing block for first pattern
+ #[rustfmt::skip]
+ let _ans = match x {
+ E::A(_) => {
+ true
+ }
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+ {
+ // lint
+ let _ans = match x {
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // should print "z" in suggestion (#6503)
+ let z = &Some(3);
+ let _z = match &z {
+ Some(3) => true,
+ _ => false,
+ };
+ }
+
+ {
+ // this could also print "z" in suggestion..?
+ let z = Some(3);
+ let _z = match &z {
+ Some(3) => true,
+ _ => false,
+ };
+ }
+
+ {
+ enum AnEnum {
+ X,
+ Y,
+ }
+
+ fn foo(_x: AnEnum) {}
+
+ fn main() {
+ let z = AnEnum::X;
+ // we can't remove the reference here!
+ let _ = match &z {
+ AnEnum::X => true,
+ _ => false,
+ };
+ foo(z);
+ }
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ // we need the reference here because later val is consumed by fun()
+ let _res = match &val {
+ &Some(ref _a) => true,
+ _ => false,
+ };
+ fun(val);
+ }
+
+ {
+ struct S(i32);
+
+ fn fun(_val: Option<S>) {}
+ let val = Some(S(42));
+ let _res = match &val {
+ &Some(ref _a) => true,
+ _ => false,
+ };
+ fun(val);
+ }
+
+ {
+ enum E {
+ A,
+ B,
+ C,
+ }
+
+ let _ = match E::A {
+ E::B => true,
+ #[cfg(feature = "foo")]
+ E::A => true,
+ _ => false,
+ };
+ }
+}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
new file mode 100644
index 000000000..e94555e27
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
@@ -0,0 +1,137 @@
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:10:14
+ |
+LL | let _y = match x {
+ | ______________^
+LL | | Some(0) => true,
+LL | | _ => false,
+LL | | };
+ | |_____^ help: try this: `matches!(x, Some(0))`
+ |
+ = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:16:14
+ |
+LL | let _w = match x {
+ | ______________^
+LL | | Some(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_____^ help: try this: `matches!(x, Some(_))`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/match_expr_like_matches_macro.rs:22:14
+ |
+LL | let _z = match x {
+ | ______________^
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `x.is_none()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:28:15
+ |
+LL | let _zz = match x {
+ | _______________^
+LL | | Some(r) if r == 0 => false,
+LL | | _ => true,
+LL | | };
+ | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
+
+error: if let .. else expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:34:16
+ |
+LL | let _zzz = if let Some(5) = x { true } else { false };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:58:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::A(_) => true,
+LL | | E::B(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:68:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::A(_) => {
+LL | | true
+LL | | }
+LL | | E::B(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:78:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::B(_) => false,
+LL | | E::C => false,
+LL | | _ => true,
+LL | | };
+ | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:138:18
+ |
+LL | let _z = match &z {
+ | __________________^
+LL | | Some(3) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(z, Some(3))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:147:18
+ |
+LL | let _z = match &z {
+ | __________________^
+LL | | Some(3) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(&z, Some(3))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:164:21
+ |
+LL | let _ = match &z {
+ | _____________________^
+LL | | AnEnum::X => true,
+LL | | _ => false,
+LL | | };
+ | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:178:20
+ |
+LL | let _res = match &val {
+ | ____________________^
+LL | | &Some(ref _a) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:190:20
+ |
+LL | let _res = match &val {
+ | ____________________^
+LL | | &Some(ref _a) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs
new file mode 100644
index 000000000..30415e3b9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_on_vec_items.rs
@@ -0,0 +1,152 @@
+#![warn(clippy::match_on_vec_items)]
+
+fn match_with_wildcard() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 1;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_without_wildcard() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 2;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ num => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ [ref sub @ ..] => {},
+ }
+}
+
+fn match_wildcard_and_action() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => println!("Hello, World!"),
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => println!("Hello, World!"),
+ }
+}
+
+fn match_vec_ref() {
+ let arr = &vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Lint, may panic
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Lint, may panic
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_with_get() {
+ let arr = vec![0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Ok
+ match arr.get(idx) {
+ Some(0) => println!("0"),
+ Some(1) => println!("1"),
+ _ => {},
+ }
+
+ // Ok
+ match arr.get(range) {
+ Some(&[0, 1]) => println!("0 1"),
+ Some(&[1, 2]) => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_with_array() {
+ let arr = [0, 1, 2, 3];
+ let range = 1..3;
+ let idx = 3;
+
+ // Ok
+ match arr[idx] {
+ 0 => println!("0"),
+ 1 => println!("1"),
+ _ => {},
+ }
+
+ // Ok
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ _ => {},
+ }
+}
+
+fn match_with_endless_range() {
+ let arr = vec![0, 1, 2, 3];
+ let range = ..;
+
+ // Ok
+ match arr[range] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ [0, 1, 2, 3] => println!("0, 1, 2, 3"),
+ _ => {},
+ }
+
+ // Ok
+ match arr[..] {
+ [0, 1] => println!("0 1"),
+ [1, 2] => println!("1 2"),
+ [0, 1, 2, 3] => println!("0, 1, 2, 3"),
+ _ => {},
+ }
+}
+
+fn main() {
+ match_with_wildcard();
+ match_without_wildcard();
+ match_wildcard_and_action();
+ match_vec_ref();
+ match_with_get();
+ match_with_array();
+ match_with_endless_range();
+}
diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.stderr b/src/tools/clippy/tests/ui/match_on_vec_items.stderr
new file mode 100644
index 000000000..49446d715
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_on_vec_items.stderr
@@ -0,0 +1,52 @@
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:9:11
+ |
+LL | match arr[idx] {
+ | ^^^^^^^^ help: try this: `arr.get(idx)`
+ |
+ = note: `-D clippy::match-on-vec-items` implied by `-D warnings`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:16:11
+ |
+LL | match arr[range] {
+ | ^^^^^^^^^^ help: try this: `arr.get(range)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:29:11
+ |
+LL | match arr[idx] {
+ | ^^^^^^^^ help: try this: `arr.get(idx)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:36:11
+ |
+LL | match arr[range] {
+ | ^^^^^^^^^^ help: try this: `arr.get(range)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:49:11
+ |
+LL | match arr[idx] {
+ | ^^^^^^^^ help: try this: `arr.get(idx)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:56:11
+ |
+LL | match arr[range] {
+ | ^^^^^^^^^^ help: try this: `arr.get(range)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:69:11
+ |
+LL | match arr[idx] {
+ | ^^^^^^^^ help: try this: `arr.get(idx)`
+
+error: indexing into a vector may panic
+ --> $DIR/match_on_vec_items.rs:76:11
+ |
+LL | match arr[range] {
+ | ^^^^^^^^^^ help: try this: `arr.get(range)`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
new file mode 100644
index 000000000..2f85e6357
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
@@ -0,0 +1,135 @@
+#![feature(exclusive_range_pattern)]
+#![feature(half_open_range_patterns)]
+#![warn(clippy::match_overlapping_arm)]
+#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+
+/// Tests for match_overlapping_arm
+
+fn overlapping() {
+ const FOO: u64 = 2;
+
+ match 42 {
+ 0..=10 => println!("0..=10"),
+ 0..=11 => println!("0..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=5 => println!("0..=5"),
+ 6..=7 => println!("6..=7"),
+ FOO..=11 => println!("FOO..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=5 => println!("0..=5"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..=2 => println!("0..=2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..=10 => println!("0..=10"),
+ 11..=50 => println!("11..=50"),
+ _ => (),
+ }
+
+ match 42 {
+ 2 => println!("2"),
+ 0..2 => println!("0..2"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..10 => println!("0..10"),
+ 10..50 => println!("10..50"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..11 => println!("0..11"),
+ 0..=11 => println!("0..=11"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..7 => println!("5..7"),
+ 0..10 => println!("0..10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..10 => println!("5..10"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..14 => println!("0..14"),
+ 5..10 => println!("5..10"),
+ _ => (),
+ }
+
+ match 42 {
+ 5..14 => println!("5..14"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 0..7 => println!("0..7"),
+ 0..=10 => println!("0..=10"),
+ _ => (),
+ }
+
+ match 42 {
+ 3.. => println!("3.."),
+ 0.. => println!("0.."),
+ _ => (),
+ }
+
+ match 42 {
+ ..=23 => println!("..=23"),
+ ..26 => println!("..26"),
+ _ => (),
+ }
+
+ // Issue #7816 - overlap after included range
+ match 42 {
+ 5..=10 => (),
+ 0..=20 => (),
+ 21..=30 => (),
+ 21..=40 => (),
+ _ => (),
+ }
+
+ // Issue #7829
+ match 0 {
+ -1..=1 => (),
+ -2..=2 => (),
+ _ => (),
+ }
+
+ // Only warn about the first if there are multiple overlaps
+ match 42u128 {
+ 0..=0x0000_0000_0000_00ff => (),
+ 0..=0x0000_0000_0000_ffff => (),
+ 0..=0x0000_0000_ffff_ffff => (),
+ 0..=0xffff_ffff_ffff_ffff => (),
+ _ => (),
+ }
+
+ if let None = Some(42) {
+ // nothing
+ } else if let None = Some(42) {
+ // another nothing :-)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
new file mode 100644
index 000000000..b81bb1ecf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
@@ -0,0 +1,99 @@
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:13:9
+ |
+LL | 0..=10 => println!("0..=10"),
+ | ^^^^^^
+ |
+ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:14:9
+ |
+LL | 0..=11 => println!("0..=11"),
+ | ^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:19:9
+ |
+LL | 0..=5 => println!("0..=5"),
+ | ^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:21:9
+ |
+LL | FOO..=11 => println!("FOO..=11"),
+ | ^^^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:56:9
+ |
+LL | 0..11 => println!("0..11"),
+ | ^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:57:9
+ |
+LL | 0..=11 => println!("0..=11"),
+ | ^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:81:9
+ |
+LL | 0..=10 => println!("0..=10"),
+ | ^^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:80:9
+ |
+LL | 5..14 => println!("5..14"),
+ | ^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:86:9
+ |
+LL | 0..7 => println!("0..7"),
+ | ^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:87:9
+ |
+LL | 0..=10 => println!("0..=10"),
+ | ^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:98:9
+ |
+LL | ..=23 => println!("..=23"),
+ | ^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:99:9
+ |
+LL | ..26 => println!("..26"),
+ | ^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:107:9
+ |
+LL | 21..=30 => (),
+ | ^^^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:108:9
+ |
+LL | 21..=40 => (),
+ | ^^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:121:9
+ |
+LL | 0..=0x0000_0000_0000_00ff => (),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:122:9
+ |
+LL | 0..=0x0000_0000_0000_ffff => (),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.fixed b/src/tools/clippy/tests/ui/match_ref_pats.fixed
new file mode 100644
index 000000000..1b6c2d924
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_ref_pats.fixed
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::match_ref_pats)]
+#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)]
+
+fn ref_pats() {
+ {
+ let v = &Some(0);
+ match *v {
+ Some(v) => println!("{:?}", v),
+ None => println!("none"),
+ }
+ match v {
+ // This doesn't trigger; we have a different pattern.
+ &Some(v) => println!("some"),
+ other => println!("other"),
+ }
+ }
+ let tup = &(1, 2);
+ match tup {
+ &(v, 1) => println!("{}", v),
+ _ => println!("none"),
+ }
+ // Special case: using `&` both in expr and pats.
+ let w = Some(0);
+ match w {
+ Some(v) => println!("{:?}", v),
+ None => println!("none"),
+ }
+ // False positive: only wildcard pattern.
+ let w = Some(0);
+ #[allow(clippy::match_single_binding)]
+ match w {
+ _ => println!("none"),
+ }
+
+ let a = &Some(0);
+ if a.is_none() {
+ println!("none");
+ }
+
+ let b = Some(0);
+ if b.is_none() {
+ println!("none");
+ }
+}
+
+mod ice_3719 {
+ macro_rules! foo_variant(
+ ($idx:expr) => (Foo::get($idx).unwrap())
+ );
+
+ enum Foo {
+ A,
+ B,
+ }
+
+ impl Foo {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&Foo::A),
+ 1 => Some(&Foo::B),
+ _ => None,
+ }
+ }
+ }
+
+ fn ice_3719() {
+ // ICE #3719
+ match foo_variant!(0) {
+ &Foo::A => println!("A"),
+ _ => println!("Wild"),
+ }
+ }
+}
+
+mod issue_7740 {
+ macro_rules! foobar_variant(
+ ($idx:expr) => (FooBar::get($idx).unwrap())
+ );
+
+ enum FooBar {
+ Foo,
+ Bar,
+ FooBar,
+ BarFoo,
+ }
+
+ impl FooBar {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&FooBar::Foo),
+ 1 => Some(&FooBar::Bar),
+ 2 => Some(&FooBar::FooBar),
+ 3 => Some(&FooBar::BarFoo),
+ _ => None,
+ }
+ }
+ }
+
+ fn issue_7740() {
+ // Issue #7740
+ match *foobar_variant!(0) {
+ FooBar::Foo => println!("Foo"),
+ FooBar::Bar => println!("Bar"),
+ FooBar::FooBar => println!("FooBar"),
+ _ => println!("Wild"),
+ }
+
+ // This shouldn't trigger
+ if let &FooBar::BarFoo = foobar_variant!(3) {
+ println!("BarFoo");
+ } else {
+ println!("Wild");
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.rs b/src/tools/clippy/tests/ui/match_ref_pats.rs
new file mode 100644
index 000000000..68dfac4e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_ref_pats.rs
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::match_ref_pats)]
+#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)]
+
+fn ref_pats() {
+ {
+ let v = &Some(0);
+ match v {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ match v {
+ // This doesn't trigger; we have a different pattern.
+ &Some(v) => println!("some"),
+ other => println!("other"),
+ }
+ }
+ let tup = &(1, 2);
+ match tup {
+ &(v, 1) => println!("{}", v),
+ _ => println!("none"),
+ }
+ // Special case: using `&` both in expr and pats.
+ let w = Some(0);
+ match &w {
+ &Some(v) => println!("{:?}", v),
+ &None => println!("none"),
+ }
+ // False positive: only wildcard pattern.
+ let w = Some(0);
+ #[allow(clippy::match_single_binding)]
+ match w {
+ _ => println!("none"),
+ }
+
+ let a = &Some(0);
+ if let &None = a {
+ println!("none");
+ }
+
+ let b = Some(0);
+ if let &None = &b {
+ println!("none");
+ }
+}
+
+mod ice_3719 {
+ macro_rules! foo_variant(
+ ($idx:expr) => (Foo::get($idx).unwrap())
+ );
+
+ enum Foo {
+ A,
+ B,
+ }
+
+ impl Foo {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&Foo::A),
+ 1 => Some(&Foo::B),
+ _ => None,
+ }
+ }
+ }
+
+ fn ice_3719() {
+ // ICE #3719
+ match foo_variant!(0) {
+ &Foo::A => println!("A"),
+ _ => println!("Wild"),
+ }
+ }
+}
+
+mod issue_7740 {
+ macro_rules! foobar_variant(
+ ($idx:expr) => (FooBar::get($idx).unwrap())
+ );
+
+ enum FooBar {
+ Foo,
+ Bar,
+ FooBar,
+ BarFoo,
+ }
+
+ impl FooBar {
+ fn get(idx: u8) -> Option<&'static Self> {
+ match idx {
+ 0 => Some(&FooBar::Foo),
+ 1 => Some(&FooBar::Bar),
+ 2 => Some(&FooBar::FooBar),
+ 3 => Some(&FooBar::BarFoo),
+ _ => None,
+ }
+ }
+ }
+
+ fn issue_7740() {
+ // Issue #7740
+ match foobar_variant!(0) {
+ &FooBar::Foo => println!("Foo"),
+ &FooBar::Bar => println!("Bar"),
+ &FooBar::FooBar => println!("FooBar"),
+ _ => println!("Wild"),
+ }
+
+ // This shouldn't trigger
+ if let &FooBar::BarFoo = foobar_variant!(3) {
+ println!("BarFoo");
+ } else {
+ println!("Wild");
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr
new file mode 100644
index 000000000..353f7399d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr
@@ -0,0 +1,68 @@
+error: you don't need to add `&` to all patterns
+ --> $DIR/match_ref_pats.rs:8:9
+ |
+LL | / match v {
+LL | | &Some(v) => println!("{:?}", v),
+LL | | &None => println!("none"),
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::match-ref-pats` implied by `-D warnings`
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL ~ match *v {
+LL ~ Some(v) => println!("{:?}", v),
+LL ~ None => println!("none"),
+ |
+
+error: you don't need to add `&` to both the expression and the patterns
+ --> $DIR/match_ref_pats.rs:25:5
+ |
+LL | / match &w {
+LL | | &Some(v) => println!("{:?}", v),
+LL | | &None => println!("none"),
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ match w {
+LL ~ Some(v) => println!("{:?}", v),
+LL ~ None => println!("none"),
+ |
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/match_ref_pats.rs:37:12
+ |
+LL | if let &None = a {
+ | -------^^^^^---- help: try this: `if a.is_none()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/match_ref_pats.rs:42:12
+ |
+LL | if let &None = &b {
+ | -------^^^^^----- help: try this: `if b.is_none()`
+
+error: you don't need to add `&` to all patterns
+ --> $DIR/match_ref_pats.rs:102:9
+ |
+LL | / match foobar_variant!(0) {
+LL | | &FooBar::Foo => println!("Foo"),
+LL | | &FooBar::Bar => println!("Bar"),
+LL | | &FooBar::FooBar => println!("FooBar"),
+LL | | _ => println!("Wild"),
+LL | | }
+ | |_________^
+ |
+help: instead of prefixing all patterns with `&`, you can dereference the expression
+ |
+LL ~ match *foobar_variant!(0) {
+LL ~ FooBar::Foo => println!("Foo"),
+LL ~ FooBar::Bar => println!("Bar"),
+LL ~ FooBar::FooBar => println!("FooBar"),
+ |
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
new file mode 100644
index 000000000..d4760a975
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_result_ok.fixed
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+ {
+ if let Ok(y) = x . parse() {
+ return y;
+ };
+ 0
+ }
+}
+
+// Checking `while` cases
+
+struct Wat {
+ counter: i32,
+}
+
+impl Wat {
+ fn next(&mut self) -> Result<i32, &str> {
+ self.counter += 1;
+ if self.counter < 5 {
+ Ok(self.counter)
+ } else {
+ Err("Oh no")
+ }
+ }
+}
+
+fn base_1(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_2(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+ // Expected to stay as is
+ while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_result_ok.rs b/src/tools/clippy/tests/ui/match_result_ok.rs
new file mode 100644
index 000000000..0b818723d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_result_ok.rs
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+ if let Some(y) = x.parse().ok() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+ {
+ if let Some(y) = x . parse() . ok () {
+ return y;
+ };
+ 0
+ }
+}
+
+// Checking `while` cases
+
+struct Wat {
+ counter: i32,
+}
+
+impl Wat {
+ fn next(&mut self) -> Result<i32, &str> {
+ self.counter += 1;
+ if self.counter < 5 {
+ Ok(self.counter)
+ } else {
+ Err("Oh no")
+ }
+ }
+}
+
+fn base_1(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Some(a) = wat.next().ok() {
+ println!("{}", a);
+ }
+}
+
+fn base_2(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+ // Expected to stay as is
+ while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
new file mode 100644
index 000000000..cc3bc8c76
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -0,0 +1,36 @@
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:10:5
+ |
+LL | if let Some(y) = x.parse().ok() { y } else { 0 }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::match-result-ok` implied by `-D warnings`
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+ |
+LL | if let Ok(y) = x.parse() { y } else { 0 }
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:20:9
+ |
+LL | if let Some(y) = x . parse() . ok () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+ |
+LL | if let Ok(y) = x . parse() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:46:5
+ |
+LL | while let Some(a) = wat.next().ok() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider matching on `Ok(a)` and removing the call to `ok` instead
+ |
+LL | while let Ok(a) = wat.next() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs
new file mode 100644
index 000000000..0b9342c9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms.rs
@@ -0,0 +1,56 @@
+#![warn(clippy::match_same_arms)]
+
+pub enum Abc {
+ A,
+ B,
+ C,
+}
+
+fn match_same_arms() {
+ let _ = match Abc::A {
+ Abc::A => 0,
+ Abc::B => 1,
+ _ => 0, //~ ERROR match arms have same body
+ };
+
+ match (1, 2, 3) {
+ (1, .., 3) => 42,
+ (.., 3) => 42, //~ ERROR match arms have same body
+ _ => 0,
+ };
+
+ let _ = match 42 {
+ 42 => 1,
+ 51 => 1, //~ ERROR match arms have same body
+ 41 => 2,
+ 52 => 2, //~ ERROR match arms have same body
+ _ => 0,
+ };
+
+ let _ = match 42 {
+ 1 => 2,
+ 2 => 2, //~ ERROR 2nd matched arms have same body
+ 3 => 2, //~ ERROR 3rd matched arms have same body
+ 4 => 3,
+ _ => 0,
+ };
+}
+
+mod issue4244 {
+ #[derive(PartialEq, PartialOrd, Eq, Ord)]
+ pub enum CommandInfo {
+ BuiltIn { name: String, about: Option<String> },
+ External { name: String, path: std::path::PathBuf },
+ }
+
+ impl CommandInfo {
+ pub fn name(&self) -> String {
+ match self {
+ CommandInfo::BuiltIn { name, .. } => name.to_string(),
+ CommandInfo::External { name, .. } => name.to_string(),
+ }
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr
new file mode 100644
index 000000000..b6d04263b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms.stderr
@@ -0,0 +1,121 @@
+error: this match arm has an identical body to the `_` wildcard arm
+ --> $DIR/match_same_arms.rs:11:9
+ |
+LL | Abc::A => 0,
+ | ^^^^^^^^^^^ help: try removing the arm
+ |
+ = note: `-D clippy::match-same-arms` implied by `-D warnings`
+ = help: or try changing either arm body
+note: `_` wildcard arm here
+ --> $DIR/match_same_arms.rs:13:9
+ |
+LL | _ => 0, //~ ERROR match arms have same body
+ | ^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:17:9
+ |
+LL | (1, .., 3) => 42,
+ | ----------^^^^^^
+ | |
+ | help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:18:9
+ |
+LL | (.., 3) => 42, //~ ERROR match arms have same body
+ | ^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:24:9
+ |
+LL | 51 => 1, //~ ERROR match arms have same body
+ | --^^^^^
+ | |
+ | help: try merging the arm patterns: `51 | 42`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:23:9
+ |
+LL | 42 => 1,
+ | ^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:25:9
+ |
+LL | 41 => 2,
+ | --^^^^^
+ | |
+ | help: try merging the arm patterns: `41 | 52`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:26:9
+ |
+LL | 52 => 2, //~ ERROR match arms have same body
+ | ^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:32:9
+ |
+LL | 2 => 2, //~ ERROR 2nd matched arms have same body
+ | -^^^^^
+ | |
+ | help: try merging the arm patterns: `2 | 1`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:31:9
+ |
+LL | 1 => 2,
+ | ^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:33:9
+ |
+LL | 3 => 2, //~ ERROR 3rd matched arms have same body
+ | -^^^^^
+ | |
+ | help: try merging the arm patterns: `3 | 1`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:31:9
+ |
+LL | 1 => 2,
+ | ^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:32:9
+ |
+LL | 2 => 2, //~ ERROR 2nd matched arms have same body
+ | -^^^^^
+ | |
+ | help: try merging the arm patterns: `2 | 3`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:33:9
+ |
+LL | 3 => 2, //~ ERROR 3rd matched arms have same body
+ | ^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms.rs:50:17
+ |
+LL | CommandInfo::External { name, .. } => name.to_string(),
+ | ----------------------------------^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms.rs:49:17
+ |
+LL | CommandInfo::BuiltIn { name, .. } => name.to_string(),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs
new file mode 100644
index 000000000..7aba5b447
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms2.rs
@@ -0,0 +1,238 @@
+#![warn(clippy::match_same_arms)]
+#![allow(clippy::blacklisted_name, clippy::diverging_sub_expression)]
+
+fn bar<T>(_: T) {}
+fn foo() -> bool {
+ unimplemented!()
+}
+
+fn match_same_arms() {
+ let _ = match 42 {
+ 42 => {
+ foo();
+ let mut a = 42 + [23].len() as i32;
+ if true {
+ a += 7;
+ }
+ a = -31 - a;
+ a
+ },
+ _ => {
+ //~ ERROR match arms have same body
+ foo();
+ let mut a = 42 + [23].len() as i32;
+ if true {
+ a += 7;
+ }
+ a = -31 - a;
+ a
+ },
+ };
+
+ let _ = match 42 {
+ 42 => foo(),
+ 51 => foo(), //~ ERROR match arms have same body
+ _ => true,
+ };
+
+ let _ = match Some(42) {
+ Some(_) => 24,
+ None => 24, //~ ERROR match arms have same body
+ };
+
+ let _ = match Some(42) {
+ Some(foo) => 24,
+ None => 24,
+ };
+
+ let _ = match Some(42) {
+ Some(42) => 24,
+ Some(a) => 24, // bindings are different
+ None => 0,
+ };
+
+ let _ = match Some(42) {
+ Some(a) if a > 0 => 24,
+ Some(a) => 24, // one arm has a guard
+ None => 0,
+ };
+
+ match (Some(42), Some(42)) {
+ (Some(a), None) => bar(a),
+ (None, Some(a)) => bar(a), //~ ERROR match arms have same body
+ _ => (),
+ }
+
+ match (Some(42), Some(42)) {
+ (Some(a), ..) => bar(a),
+ (.., Some(a)) => bar(a), //~ ERROR match arms have same body
+ _ => (),
+ }
+
+ let _ = match Some(()) {
+ Some(()) => 0.0,
+ None => -0.0,
+ };
+
+ match (Some(42), Some("")) {
+ (Some(a), None) => bar(a),
+ (None, Some(a)) => bar(a), // bindings have different types
+ _ => (),
+ }
+
+ let x: Result<i32, &str> = Ok(3);
+
+ // No warning because of the guard.
+ match x {
+ Ok(x) if x * x == 64 => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => println!("err"),
+ }
+
+ // This used to be a false positive; see issue #1996.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(x) if x * x == 64 => println!("ok 64"),
+ Ok(_) => println!("ok"),
+ Err(_) => println!("err"),
+ }
+
+ match (x, Some(1i32)) {
+ (Ok(x), Some(_)) => println!("ok {}", x),
+ (Ok(_), Some(x)) => println!("ok {}", x),
+ _ => println!("err"),
+ }
+
+ // No warning; different types for `x`.
+ match (x, Some(1.0f64)) {
+ (Ok(x), Some(_)) => println!("ok {}", x),
+ (Ok(_), Some(x)) => println!("ok {}", x),
+ _ => println!("err"),
+ }
+
+ // False negative #2251.
+ match x {
+ Ok(_tmp) => println!("ok"),
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => {
+ unreachable!();
+ },
+ }
+
+ // False positive #1390
+ macro_rules! empty {
+ ($e:expr) => {};
+ }
+ match 0 {
+ 0 => {
+ empty!(0);
+ },
+ 1 => {
+ empty!(1);
+ },
+ x => {
+ empty!(x);
+ },
+ };
+
+ // still lint if the tokens are the same
+ match 0 {
+ 0 => {
+ empty!(0);
+ },
+ 1 => {
+ empty!(0);
+ },
+ x => {
+ empty!(x);
+ },
+ }
+
+ match_expr_like_matches_macro_priority();
+}
+
+fn match_expr_like_matches_macro_priority() {
+ enum E {
+ A,
+ B,
+ C,
+ }
+ let x = E::A;
+ let _ans = match x {
+ E::A => false,
+ E::B => false,
+ _ => true,
+ };
+}
+
+fn main() {
+ let _ = match Some(0) {
+ Some(0) => 0,
+ Some(1) => 1,
+ #[cfg(feature = "foo")]
+ Some(2) => 2,
+ _ => 1,
+ };
+
+ enum Foo {
+ X(u32),
+ Y(u32),
+ Z(u32),
+ }
+
+ // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between.
+ let _ = match Foo::X(0) {
+ Foo::X(0) => 1,
+ Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2,
+ Foo::Z(_) => 1,
+ _ => 0,
+ };
+
+ // Suggest moving `Foo::Z(_)` up.
+ let _ = match Foo::X(0) {
+ Foo::X(0) => 1,
+ Foo::X(_) | Foo::Y(_) => 2,
+ Foo::Z(_) => 1,
+ _ => 0,
+ };
+
+ // Suggest moving `Foo::X(0)` down.
+ let _ = match Foo::X(0) {
+ Foo::X(0) => 1,
+ Foo::Y(_) | Foo::Z(0) => 2,
+ Foo::Z(_) => 1,
+ _ => 0,
+ };
+
+ // Don't lint.
+ let _ = match 0 {
+ -2 => 1,
+ -5..=50 => 2,
+ -150..=88 => 1,
+ _ => 3,
+ };
+
+ struct Bar {
+ x: u32,
+ y: u32,
+ z: u32,
+ }
+
+ // Lint.
+ let _ = match None {
+ Some(Bar { x: 0, y: 5, .. }) => 1,
+ Some(Bar { y: 10, z: 0, .. }) => 2,
+ None => 50,
+ Some(Bar { y: 0, x: 5, .. }) => 1,
+ _ => 200,
+ };
+
+ let _ = match 0 {
+ 0 => todo!(),
+ 1 => todo!(),
+ 2 => core::convert::identity::<u32>(todo!()),
+ 3 => core::convert::identity::<u32>(todo!()),
+ _ => 5,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr
new file mode 100644
index 000000000..14a672ba2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr
@@ -0,0 +1,196 @@
+error: this match arm has an identical body to the `_` wildcard arm
+ --> $DIR/match_same_arms2.rs:11:9
+ |
+LL | / 42 => {
+LL | | foo();
+LL | | let mut a = 42 + [23].len() as i32;
+LL | | if true {
+... |
+LL | | a
+LL | | },
+ | |_________^ help: try removing the arm
+ |
+ = note: `-D clippy::match-same-arms` implied by `-D warnings`
+ = help: or try changing either arm body
+note: `_` wildcard arm here
+ --> $DIR/match_same_arms2.rs:20:9
+ |
+LL | / _ => {
+LL | | //~ ERROR match arms have same body
+LL | | foo();
+LL | | let mut a = 42 + [23].len() as i32;
+... |
+LL | | a
+LL | | },
+ | |_________^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:34:9
+ |
+LL | 51 => foo(), //~ ERROR match arms have same body
+ | --^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `51 | 42`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:33:9
+ |
+LL | 42 => foo(),
+ | ^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:40:9
+ |
+LL | None => 24, //~ ERROR match arms have same body
+ | ----^^^^^^
+ | |
+ | help: try merging the arm patterns: `None | Some(_)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:39:9
+ |
+LL | Some(_) => 24,
+ | ^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:62:9
+ |
+LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body
+ | ---------------^^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:61:9
+ |
+LL | (Some(a), None) => bar(a),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:67:9
+ |
+LL | (Some(a), ..) => bar(a),
+ | -------------^^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:68:9
+ |
+LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:101:9
+ |
+LL | (Ok(x), Some(_)) => println!("ok {}", x),
+ | ----------------^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:102:9
+ |
+LL | (Ok(_), Some(x)) => println!("ok {}", x),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:117:9
+ |
+LL | Ok(_) => println!("ok"),
+ | -----^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try merging the arm patterns: `Ok(_) | Ok(3)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:116:9
+ |
+LL | Ok(3) => println!("ok"),
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:144:9
+ |
+LL | 1 => {
+ | ^ help: try merging the arm patterns: `1 | 0`
+ | _________|
+ | |
+LL | | empty!(0);
+LL | | },
+ | |_________^
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:141:9
+ |
+LL | / 0 => {
+LL | | empty!(0);
+LL | | },
+ | |_________^
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_same_arms2.rs:162:16
+ |
+LL | let _ans = match x {
+ | ________________^
+LL | | E::A => false,
+LL | | E::B => false,
+LL | | _ => true,
+LL | | };
+ | |_____^ help: try this: `!matches!(x, E::A | E::B)`
+ |
+ = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:194:9
+ |
+LL | Foo::X(0) => 1,
+ | ---------^^^^^
+ | |
+ | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:196:9
+ |
+LL | Foo::Z(_) => 1,
+ | ^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:204:9
+ |
+LL | Foo::Z(_) => 1,
+ | ---------^^^^^
+ | |
+ | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:202:9
+ |
+LL | Foo::X(0) => 1,
+ | ^^^^^^^^^^^^^^
+
+error: this match arm has an identical body to another arm
+ --> $DIR/match_same_arms2.rs:227:9
+ |
+LL | Some(Bar { y: 0, x: 5, .. }) => 1,
+ | ----------------------------^^^^^
+ | |
+ | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })`
+ |
+ = help: or try changing either arm body
+note: other arm here
+ --> $DIR/match_same_arms2.rs:224:9
+ |
+LL | Some(Bar { x: 0, y: 5, .. }) => 1,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
new file mode 100644
index 000000000..de46e6cff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -0,0 +1,126 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn coords() -> Point {
+ Point { x: 1, y: 2 }
+}
+
+macro_rules! foo {
+ ($param:expr) => {
+ match $param {
+ _ => println!("whatever"),
+ }
+ };
+}
+
+fn main() {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ // Lint
+ let (x, y, z) = (a, b, c);
+ {
+ println!("{} {} {}", x, y, z);
+ }
+ // Lint
+ let (x, y, z) = (a, b, c);
+ println!("{} {} {}", x, y, z);
+ // Ok
+ foo!(a);
+ // Ok
+ match a {
+ 2 => println!("2"),
+ _ => println!("Not 2"),
+ }
+ // Ok
+ let d = Some(5);
+ match d {
+ Some(d) => println!("{}", d),
+ _ => println!("None"),
+ }
+ // Lint
+ println!("whatever");
+ // Lint
+ {
+ let x = 29;
+ println!("x has a value of {}", x);
+ }
+ // Lint
+ {
+ let e = 5 * a;
+ if e >= 5 {
+ println!("e is superior to 5");
+ }
+ }
+ // Lint
+ let p = Point { x: 0, y: 7 };
+ let Point { x, y } = p;
+ println!("Coords: ({}, {})", x, y);
+ // Lint
+ let Point { x: x1, y: y1 } = p;
+ println!("Coords: ({}, {})", x1, y1);
+ // Lint
+ let x = 5;
+ let ref r = x;
+ println!("Got a reference to {}", r);
+ // Lint
+ let mut x = 5;
+ let ref mut mr = x;
+ println!("Got a mutable reference to {}", mr);
+ // Lint
+ let Point { x, y } = coords();
+ let product = x * y;
+ // Lint
+ let v = vec![Some(1), Some(2), Some(3), Some(4)];
+ #[allow(clippy::let_and_return)]
+ let _ = v
+ .iter()
+ .map(|i| {
+ let unwrapped = i.unwrap();
+ unwrapped
+ })
+ .collect::<Vec<u8>>();
+ // Ok
+ let x = 1;
+ match x {
+ #[cfg(disabled_feature)]
+ 0 => println!("Disabled branch"),
+ _ => println!("Enabled branch"),
+ }
+
+ // Ok
+ let x = 1;
+ let y = 1;
+ match match y {
+ 0 => 1,
+ _ => 2,
+ } {
+ #[cfg(disabled_feature)]
+ 0 => println!("Array index start"),
+ _ => println!("Not an array index start"),
+ }
+
+ // Lint
+ let x = 1;
+ println!("Not an array index start");
+}
+
+#[allow(dead_code)]
+fn issue_8723() {
+ let (mut val, idx) = ("a b", 1);
+
+ let (pre, suf) = val.split_at(idx);
+ val = {
+ println!("{}", pre);
+ suf
+ };
+
+ let _ = val;
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
new file mode 100644
index 000000000..eea64fcb2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -0,0 +1,142 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn coords() -> Point {
+ Point { x: 1, y: 2 }
+}
+
+macro_rules! foo {
+ ($param:expr) => {
+ match $param {
+ _ => println!("whatever"),
+ }
+ };
+}
+
+fn main() {
+ let a = 1;
+ let b = 2;
+ let c = 3;
+ // Lint
+ match (a, b, c) {
+ (x, y, z) => {
+ println!("{} {} {}", x, y, z);
+ },
+ }
+ // Lint
+ match (a, b, c) {
+ (x, y, z) => println!("{} {} {}", x, y, z),
+ }
+ // Ok
+ foo!(a);
+ // Ok
+ match a {
+ 2 => println!("2"),
+ _ => println!("Not 2"),
+ }
+ // Ok
+ let d = Some(5);
+ match d {
+ Some(d) => println!("{}", d),
+ _ => println!("None"),
+ }
+ // Lint
+ match a {
+ _ => println!("whatever"),
+ }
+ // Lint
+ match a {
+ _ => {
+ let x = 29;
+ println!("x has a value of {}", x);
+ },
+ }
+ // Lint
+ match a {
+ _ => {
+ let e = 5 * a;
+ if e >= 5 {
+ println!("e is superior to 5");
+ }
+ },
+ }
+ // Lint
+ let p = Point { x: 0, y: 7 };
+ match p {
+ Point { x, y } => println!("Coords: ({}, {})", x, y),
+ }
+ // Lint
+ match p {
+ Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
+ }
+ // Lint
+ let x = 5;
+ match x {
+ ref r => println!("Got a reference to {}", r),
+ }
+ // Lint
+ let mut x = 5;
+ match x {
+ ref mut mr => println!("Got a mutable reference to {}", mr),
+ }
+ // Lint
+ let product = match coords() {
+ Point { x, y } => x * y,
+ };
+ // Lint
+ let v = vec![Some(1), Some(2), Some(3), Some(4)];
+ #[allow(clippy::let_and_return)]
+ let _ = v
+ .iter()
+ .map(|i| match i.unwrap() {
+ unwrapped => unwrapped,
+ })
+ .collect::<Vec<u8>>();
+ // Ok
+ let x = 1;
+ match x {
+ #[cfg(disabled_feature)]
+ 0 => println!("Disabled branch"),
+ _ => println!("Enabled branch"),
+ }
+
+ // Ok
+ let x = 1;
+ let y = 1;
+ match match y {
+ 0 => 1,
+ _ => 2,
+ } {
+ #[cfg(disabled_feature)]
+ 0 => println!("Array index start"),
+ _ => println!("Not an array index start"),
+ }
+
+ // Lint
+ let x = 1;
+ match x {
+ // =>
+ _ => println!("Not an array index start"),
+ }
+}
+
+#[allow(dead_code)]
+fn issue_8723() {
+ let (mut val, idx) = ("a b", 1);
+
+ val = match val.split_at(idx) {
+ (pre, suf) => {
+ println!("{}", pre);
+ suf
+ },
+ };
+
+ let _ = val;
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
new file mode 100644
index 000000000..5d4e7314b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -0,0 +1,200 @@
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:28:5
+ |
+LL | / match (a, b, c) {
+LL | | (x, y, z) => {
+LL | | println!("{} {} {}", x, y, z);
+LL | | },
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::match-single-binding` implied by `-D warnings`
+help: consider using a `let` statement
+ |
+LL ~ let (x, y, z) = (a, b, c);
+LL + {
+LL + println!("{} {} {}", x, y, z);
+LL + }
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:34:5
+ |
+LL | / match (a, b, c) {
+LL | | (x, y, z) => println!("{} {} {}", x, y, z),
+LL | | }
+ | |_____^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let (x, y, z) = (a, b, c);
+LL + println!("{} {} {}", x, y, z);
+ |
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:51:5
+ |
+LL | / match a {
+LL | | _ => println!("whatever"),
+LL | | }
+ | |_____^ help: consider using the match body instead: `println!("whatever");`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:55:5
+ |
+LL | / match a {
+LL | | _ => {
+LL | | let x = 29;
+LL | | println!("x has a value of {}", x);
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: consider using the match body instead
+ |
+LL ~ {
+LL + let x = 29;
+LL + println!("x has a value of {}", x);
+LL + }
+ |
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:62:5
+ |
+LL | / match a {
+LL | | _ => {
+LL | | let e = 5 * a;
+LL | | if e >= 5 {
+... |
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: consider using the match body instead
+ |
+LL ~ {
+LL + let e = 5 * a;
+LL + if e >= 5 {
+LL + println!("e is superior to 5");
+LL + }
+LL + }
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:72:5
+ |
+LL | / match p {
+LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
+LL | | }
+ | |_____^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let Point { x, y } = p;
+LL + println!("Coords: ({}, {})", x, y);
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:76:5
+ |
+LL | / match p {
+LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
+LL | | }
+ | |_____^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let Point { x: x1, y: y1 } = p;
+LL + println!("Coords: ({}, {})", x1, y1);
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:81:5
+ |
+LL | / match x {
+LL | | ref r => println!("Got a reference to {}", r),
+LL | | }
+ | |_____^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let ref r = x;
+LL + println!("Got a reference to {}", r);
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:86:5
+ |
+LL | / match x {
+LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
+LL | | }
+ | |_____^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let ref mut mr = x;
+LL + println!("Got a mutable reference to {}", mr);
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:90:5
+ |
+LL | / let product = match coords() {
+LL | | Point { x, y } => x * y,
+LL | | };
+ | |______^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let Point { x, y } = coords();
+LL + let product = x * y;
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding.rs:98:18
+ |
+LL | .map(|i| match i.unwrap() {
+ | __________________^
+LL | | unwrapped => unwrapped,
+LL | | })
+ | |_________^
+ |
+help: consider using a `let` statement
+ |
+LL ~ .map(|i| {
+LL + let unwrapped = i.unwrap();
+LL + unwrapped
+LL ~ })
+ |
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:124:5
+ |
+LL | / match x {
+LL | | // =>
+LL | | _ => println!("Not an array index start"),
+LL | | }
+ | |_____^ help: consider using the match body instead: `println!("Not an array index start");`
+
+error: this assignment could be simplified
+ --> $DIR/match_single_binding.rs:134:5
+ |
+LL | / val = match val.split_at(idx) {
+LL | | (pre, suf) => {
+LL | | println!("{}", pre);
+LL | | suf
+LL | | },
+LL | | };
+ | |_____^
+ |
+help: consider removing the `match` expression
+ |
+LL ~ let (pre, suf) = val.split_at(idx);
+LL + val = {
+LL + println!("{}", pre);
+LL + suf
+LL ~ };
+ |
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_single_binding2.fixed b/src/tools/clippy/tests/ui/match_single_binding2.fixed
new file mode 100644
index 000000000..a91fcc212
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding2.fixed
@@ -0,0 +1,53 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+ // Lint (additional curly braces needed, see #6572)
+ struct AppendIter<I>
+ where
+ I: Iterator,
+ {
+ inner: Option<(I, <I as Iterator>::Item)>,
+ }
+
+ #[allow(dead_code)]
+ fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+ match &iter.inner {
+ Some((iter, _item)) => {
+ let (min, max) = iter.size_hint();
+ (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+ },
+ None => (0, Some(0)),
+ }
+ }
+
+ // Lint (no additional curly braces needed)
+ let opt = Some((5, 2));
+ let get_tup = || -> (i32, i32) { (1, 2) };
+ match opt {
+ #[rustfmt::skip]
+ Some((first, _second)) => {
+ let (a, b) = get_tup();
+ println!("a {:?} and b {:?}", a, b);
+ },
+ None => println!("nothing"),
+ }
+
+ fn side_effects() {}
+
+ // Lint (scrutinee has side effects)
+ // issue #7094
+ side_effects();
+ println!("Side effects");
+
+ // Lint (scrutinee has side effects)
+ // issue #7094
+ let x = 1;
+ match x {
+ 0 => 1,
+ _ => 2,
+ };
+ println!("Single branch");
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding2.rs b/src/tools/clippy/tests/ui/match_single_binding2.rs
new file mode 100644
index 000000000..476386eba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding2.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+ // Lint (additional curly braces needed, see #6572)
+ struct AppendIter<I>
+ where
+ I: Iterator,
+ {
+ inner: Option<(I, <I as Iterator>::Item)>,
+ }
+
+ #[allow(dead_code)]
+ fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+ match &iter.inner {
+ Some((iter, _item)) => match iter.size_hint() {
+ (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+ },
+ None => (0, Some(0)),
+ }
+ }
+
+ // Lint (no additional curly braces needed)
+ let opt = Some((5, 2));
+ let get_tup = || -> (i32, i32) { (1, 2) };
+ match opt {
+ #[rustfmt::skip]
+ Some((first, _second)) => {
+ match get_tup() {
+ (a, b) => println!("a {:?} and b {:?}", a, b),
+ }
+ },
+ None => println!("nothing"),
+ }
+
+ fn side_effects() {}
+
+ // Lint (scrutinee has side effects)
+ // issue #7094
+ match side_effects() {
+ _ => println!("Side effects"),
+ }
+
+ // Lint (scrutinee has side effects)
+ // issue #7094
+ let x = 1;
+ match match x {
+ 0 => 1,
+ _ => 2,
+ } {
+ _ => println!("Single branch"),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding2.stderr b/src/tools/clippy/tests/ui/match_single_binding2.stderr
new file mode 100644
index 000000000..22bf7d8be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_single_binding2.stderr
@@ -0,0 +1,68 @@
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding2.rs:18:36
+ |
+LL | Some((iter, _item)) => match iter.size_hint() {
+ | ____________________________________^
+LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+LL | | },
+ | |_____________^
+ |
+ = note: `-D clippy::match-single-binding` implied by `-D warnings`
+help: consider using a `let` statement
+ |
+LL ~ Some((iter, _item)) => {
+LL + let (min, max) = iter.size_hint();
+LL + (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+LL ~ },
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding2.rs:31:13
+ |
+LL | / match get_tup() {
+LL | | (a, b) => println!("a {:?} and b {:?}", a, b),
+LL | | }
+ | |_____________^
+ |
+help: consider using a `let` statement
+ |
+LL ~ let (a, b) = get_tup();
+LL + println!("a {:?} and b {:?}", a, b);
+ |
+
+error: this match could be replaced by its scrutinee and body
+ --> $DIR/match_single_binding2.rs:42:5
+ |
+LL | / match side_effects() {
+LL | | _ => println!("Side effects"),
+LL | | }
+ | |_____^
+ |
+help: consider using the scrutinee and body instead
+ |
+LL ~ side_effects();
+LL + println!("Side effects");
+ |
+
+error: this match could be replaced by its scrutinee and body
+ --> $DIR/match_single_binding2.rs:49:5
+ |
+LL | / match match x {
+LL | | 0 => 1,
+LL | | _ => 2,
+LL | | } {
+LL | | _ => println!("Single branch"),
+LL | | }
+ | |_____^
+ |
+help: consider using the scrutinee and body instead
+ |
+LL ~ match x {
+LL + 0 => 1,
+LL + _ => 2,
+LL + };
+LL + println!("Single branch");
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.fixed b/src/tools/clippy/tests/ui/match_str_case_mismatch.fixed
new file mode 100644
index 000000000..e436bcf49
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.fixed
@@ -0,0 +1,186 @@
+// run-rustfix
+#![warn(clippy::match_str_case_mismatch)]
+#![allow(dead_code)]
+
+// Valid
+
+fn as_str_match() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn non_alphabetic() {
+ let var = "~!@#$%^&*()-_=+FOO";
+
+ match var.to_ascii_lowercase().as_str() {
+ "1234567890" => {},
+ "~!@#$%^&*()-_=+foo" => {},
+ "\n\r\t\x7F" => {},
+ _ => {},
+ }
+}
+
+fn unicode_cased() {
+ let var = "ВОДЫ";
+
+ match var.to_lowercase().as_str() {
+ "水" => {},
+ "νερό" => {},
+ "воды" => {},
+ "물" => {},
+ _ => {},
+ }
+}
+
+fn titlecase() {
+ let var = "BarDz";
+
+ match var.to_lowercase().as_str() {
+ "foolj" => {},
+ "bardz" => {},
+ _ => {},
+ }
+}
+
+fn no_case_equivalent() {
+ let var = "barʁ";
+
+ match var.to_uppercase().as_str() {
+ "FOOɕ" => {},
+ "BARʁ" => {},
+ _ => {},
+ }
+}
+
+fn addrof_unary_match() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+fn unrelated_method() {
+ struct Item {
+ a: String,
+ }
+
+ impl Item {
+ #[allow(clippy::wrong_self_convention)]
+ fn to_lowercase(self) -> String {
+ self.a
+ }
+ }
+
+ let item = Item { a: String::from("BAR") };
+
+ match &*item.to_lowercase() {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+// Invalid
+
+fn as_str_match_mismatch() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn non_alphabetic_mismatch() {
+ let var = "~!@#$%^&*()-_=+FOO";
+
+ match var.to_ascii_lowercase().as_str() {
+ "1234567890" => {},
+ "~!@#$%^&*()-_=+foo" => {},
+ "\n\r\t\x7F" => {},
+ _ => {},
+ }
+}
+
+fn unicode_cased_mismatch() {
+ let var = "ВОДЫ";
+
+ match var.to_lowercase().as_str() {
+ "水" => {},
+ "νερό" => {},
+ "воды" => {},
+ "물" => {},
+ _ => {},
+ }
+}
+
+fn titlecase_mismatch() {
+ let var = "BarDz";
+
+ match var.to_lowercase().as_str() {
+ "foolj" => {},
+ "bardz" => {},
+ _ => {},
+ }
+}
+
+fn no_case_equivalent_mismatch() {
+ let var = "barʁ";
+
+ match var.to_uppercase().as_str() {
+ "FOOɕ" => {},
+ "BARʁ" => {},
+ _ => {},
+ }
+}
+
+fn addrof_unary_match_mismatch() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain_mismatch() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.rs b/src/tools/clippy/tests/ui/match_str_case_mismatch.rs
new file mode 100644
index 000000000..92e2a000a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.rs
@@ -0,0 +1,186 @@
+// run-rustfix
+#![warn(clippy::match_str_case_mismatch)]
+#![allow(dead_code)]
+
+// Valid
+
+fn as_str_match() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn non_alphabetic() {
+ let var = "~!@#$%^&*()-_=+FOO";
+
+ match var.to_ascii_lowercase().as_str() {
+ "1234567890" => {},
+ "~!@#$%^&*()-_=+foo" => {},
+ "\n\r\t\x7F" => {},
+ _ => {},
+ }
+}
+
+fn unicode_cased() {
+ let var = "ВОДЫ";
+
+ match var.to_lowercase().as_str() {
+ "水" => {},
+ "νερό" => {},
+ "воды" => {},
+ "물" => {},
+ _ => {},
+ }
+}
+
+fn titlecase() {
+ let var = "BarDz";
+
+ match var.to_lowercase().as_str() {
+ "foolj" => {},
+ "bardz" => {},
+ _ => {},
+ }
+}
+
+fn no_case_equivalent() {
+ let var = "barʁ";
+
+ match var.to_uppercase().as_str() {
+ "FOOɕ" => {},
+ "BARʁ" => {},
+ _ => {},
+ }
+}
+
+fn addrof_unary_match() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+fn unrelated_method() {
+ struct Item {
+ a: String,
+ }
+
+ impl Item {
+ #[allow(clippy::wrong_self_convention)]
+ fn to_lowercase(self) -> String {
+ self.a
+ }
+ }
+
+ let item = Item { a: String::from("BAR") };
+
+ match &*item.to_lowercase() {
+ "FOO" => {},
+ "BAR" => {},
+ _ => {},
+ }
+}
+
+// Invalid
+
+fn as_str_match_mismatch() {
+ let var = "BAR";
+
+ match var.to_ascii_lowercase().as_str() {
+ "foo" => {},
+ "Bar" => {},
+ _ => {},
+ }
+}
+
+fn non_alphabetic_mismatch() {
+ let var = "~!@#$%^&*()-_=+FOO";
+
+ match var.to_ascii_lowercase().as_str() {
+ "1234567890" => {},
+ "~!@#$%^&*()-_=+Foo" => {},
+ "\n\r\t\x7F" => {},
+ _ => {},
+ }
+}
+
+fn unicode_cased_mismatch() {
+ let var = "ВОДЫ";
+
+ match var.to_lowercase().as_str() {
+ "水" => {},
+ "νερό" => {},
+ "Воды" => {},
+ "물" => {},
+ _ => {},
+ }
+}
+
+fn titlecase_mismatch() {
+ let var = "BarDz";
+
+ match var.to_lowercase().as_str() {
+ "foolj" => {},
+ "barDz" => {},
+ _ => {},
+ }
+}
+
+fn no_case_equivalent_mismatch() {
+ let var = "barʁ";
+
+ match var.to_uppercase().as_str() {
+ "FOOɕ" => {},
+ "bARʁ" => {},
+ _ => {},
+ }
+}
+
+fn addrof_unary_match_mismatch() {
+ let var = "BAR";
+
+ match &*var.to_ascii_lowercase() {
+ "foo" => {},
+ "Bar" => {},
+ _ => {},
+ }
+}
+
+fn alternating_chain_mismatch() {
+ let var = "BAR";
+
+ match &*var
+ .to_ascii_lowercase()
+ .to_uppercase()
+ .to_lowercase()
+ .to_ascii_uppercase()
+ {
+ "FOO" => {},
+ "bAR" => {},
+ _ => {},
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
new file mode 100644
index 000000000..197520a3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
@@ -0,0 +1,80 @@
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:113:9
+ |
+LL | "Bar" => {},
+ | ^^^^^
+ |
+ = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
+help: consider changing the case of this arm to respect `to_ascii_lowercase`
+ |
+LL | "bar" => {},
+ | ~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:123:9
+ |
+LL | "~!@#$%^&*()-_=+Foo" => {},
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider changing the case of this arm to respect `to_ascii_lowercase`
+ |
+LL | "~!@#$%^&*()-_=+foo" => {},
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:135:9
+ |
+LL | "Воды" => {},
+ | ^^^^^^
+ |
+help: consider changing the case of this arm to respect `to_lowercase`
+ |
+LL | "воды" => {},
+ | ~~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:146:9
+ |
+LL | "barDz" => {},
+ | ^^^^^^
+ |
+help: consider changing the case of this arm to respect `to_lowercase`
+ |
+LL | "bardz" => {},
+ | ~~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:156:9
+ |
+LL | "bARʁ" => {},
+ | ^^^^^^
+ |
+help: consider changing the case of this arm to respect `to_uppercase`
+ |
+LL | "BARʁ" => {},
+ | ~~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:166:9
+ |
+LL | "Bar" => {},
+ | ^^^^^
+ |
+help: consider changing the case of this arm to respect `to_ascii_lowercase`
+ |
+LL | "bar" => {},
+ | ~~~~~
+
+error: this `match` arm has a differing case than its expression
+ --> $DIR/match_str_case_mismatch.rs:181:9
+ |
+LL | "bAR" => {},
+ | ^^^^^
+ |
+help: consider changing the case of this arm to respect `to_ascii_uppercase`
+ |
+LL | "BAR" => {},
+ | ~~~~~
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.edition2018.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.edition2018.stderr
new file mode 100644
index 000000000..2a4012039
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wild_err_arm.edition2018.stderr
@@ -0,0 +1,35 @@
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:14:9
+ |
+LL | Err(_) => panic!("err"),
+ | ^^^^^^
+ |
+ = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:20:9
+ |
+LL | Err(_) => panic!(),
+ | ^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:26:9
+ |
+LL | Err(_) => {
+ | ^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_e)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:34:9
+ |
+LL | Err(_e) => panic!(),
+ | ^^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.edition2021.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.edition2021.stderr
new file mode 100644
index 000000000..2a4012039
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wild_err_arm.edition2021.stderr
@@ -0,0 +1,35 @@
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:14:9
+ |
+LL | Err(_) => panic!("err"),
+ | ^^^^^^
+ |
+ = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:20:9
+ |
+LL | Err(_) => panic!(),
+ | ^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:26:9
+ |
+LL | Err(_) => {
+ | ^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: `Err(_e)` matches all errors
+ --> $DIR/match_wild_err_arm.rs:34:9
+ |
+LL | Err(_e) => panic!(),
+ | ^^^^^^^
+ |
+ = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.rs b/src/tools/clippy/tests/ui/match_wild_err_arm.rs
new file mode 100644
index 000000000..0a86144b9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wild_err_arm.rs
@@ -0,0 +1,68 @@
+// revisions: edition2018 edition2021
+// [edition2018] edition:2018
+// [edition2021] edition:2021
+#![feature(exclusive_range_pattern)]
+#![allow(clippy::match_same_arms)]
+#![warn(clippy::match_wild_err_arm)]
+
+fn match_wild_err_arm() {
+ let x: Result<i32, &str> = Ok(3);
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => panic!("err"),
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => panic!(),
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => {
+ panic!();
+ },
+ }
+
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_e) => panic!(),
+ }
+
+ // Allowed when used in `panic!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_e) => panic!("{}", _e),
+ }
+
+ // Allowed when not with `panic!` block.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => println!("err"),
+ }
+
+ // Allowed when used with `unreachable!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => unreachable!(),
+ }
+
+ // Allowed when used with `unreachable!`.
+ match x {
+ Ok(3) => println!("ok"),
+ Ok(_) => println!("ok"),
+ Err(_) => {
+ unreachable!();
+ },
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed
new file mode 100644
index 000000000..e675c183e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed
@@ -0,0 +1,134 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+ A,
+ B,
+ C,
+}
+
+enum Color {
+ Red,
+ Green,
+ Blue,
+ Rgb(u8, u8, u8),
+}
+impl Color {
+ fn f(self) {
+ match self {
+ Self::Red => (),
+ Self::Green => (),
+ Self::Blue => (),
+ Self::Rgb(..) => (),
+ };
+ }
+}
+
+fn main() {
+ let f = Foo::A;
+ match f {
+ Foo::A => {},
+ Foo::B => {},
+ Foo::C => {},
+ }
+
+ let color = Color::Red;
+
+ // check exhaustive bindings
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(_r, _g, _b) => {},
+ Color::Blue => {},
+ }
+
+ // check exhaustive wild
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(..) => {},
+ Color::Blue => {},
+ }
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(_, _, _) => {},
+ Color::Blue => {},
+ }
+
+ // shouldn't lint as there is one missing variant
+ // and one that isn't exhaustively covered
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(255, _, _) => {},
+ _ => {},
+ }
+
+ // References shouldn't change anything
+ match &color {
+ &Color::Red => (),
+ Color::Green => (),
+ &Color::Rgb(..) => (),
+ Color::Blue => (),
+ }
+
+ use self::Color as C;
+
+ match color {
+ C::Red => (),
+ C::Green => (),
+ C::Rgb(..) => (),
+ C::Blue => (),
+ }
+
+ match color {
+ C::Red => (),
+ Color::Green => (),
+ Color::Rgb(..) => (),
+ Color::Blue => (),
+ }
+
+ match Some(0) {
+ Some(0) => 0,
+ Some(_) => 1,
+ _ => 2,
+ };
+
+ #[non_exhaustive]
+ enum Bar {
+ A,
+ B,
+ C,
+ }
+ match Bar::A {
+ Bar::A => (),
+ Bar::B => (),
+ _ => (),
+ };
+
+ //#6984
+ {
+ #![allow(clippy::manual_non_exhaustive)]
+ pub enum Enum {
+ A,
+ B,
+ C,
+ #[doc(hidden)]
+ __Private,
+ }
+ match Enum::A {
+ Enum::A => (),
+ Enum::B => (),
+ Enum::C => (),
+ _ => (),
+ }
+ match Enum::A {
+ Enum::A => (),
+ Enum::B => (),
+ _ => (),
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs
new file mode 100644
index 000000000..38c3ffc00
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs
@@ -0,0 +1,134 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+ A,
+ B,
+ C,
+}
+
+enum Color {
+ Red,
+ Green,
+ Blue,
+ Rgb(u8, u8, u8),
+}
+impl Color {
+ fn f(self) {
+ match self {
+ Self::Red => (),
+ Self::Green => (),
+ Self::Blue => (),
+ _ => (),
+ };
+ }
+}
+
+fn main() {
+ let f = Foo::A;
+ match f {
+ Foo::A => {},
+ Foo::B => {},
+ _ => {},
+ }
+
+ let color = Color::Red;
+
+ // check exhaustive bindings
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(_r, _g, _b) => {},
+ _ => {},
+ }
+
+ // check exhaustive wild
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(..) => {},
+ _ => {},
+ }
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(_, _, _) => {},
+ _ => {},
+ }
+
+ // shouldn't lint as there is one missing variant
+ // and one that isn't exhaustively covered
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Rgb(255, _, _) => {},
+ _ => {},
+ }
+
+ // References shouldn't change anything
+ match &color {
+ &Color::Red => (),
+ Color::Green => (),
+ &Color::Rgb(..) => (),
+ &_ => (),
+ }
+
+ use self::Color as C;
+
+ match color {
+ C::Red => (),
+ C::Green => (),
+ C::Rgb(..) => (),
+ _ => (),
+ }
+
+ match color {
+ C::Red => (),
+ Color::Green => (),
+ Color::Rgb(..) => (),
+ _ => (),
+ }
+
+ match Some(0) {
+ Some(0) => 0,
+ Some(_) => 1,
+ _ => 2,
+ };
+
+ #[non_exhaustive]
+ enum Bar {
+ A,
+ B,
+ C,
+ }
+ match Bar::A {
+ Bar::A => (),
+ Bar::B => (),
+ _ => (),
+ };
+
+ //#6984
+ {
+ #![allow(clippy::manual_non_exhaustive)]
+ pub enum Enum {
+ A,
+ B,
+ C,
+ #[doc(hidden)]
+ __Private,
+ }
+ match Enum::A {
+ Enum::A => (),
+ Enum::B => (),
+ Enum::C => (),
+ _ => (),
+ }
+ match Enum::A {
+ Enum::A => (),
+ Enum::B => (),
+ _ => (),
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr
new file mode 100644
index 000000000..34538dea8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr
@@ -0,0 +1,52 @@
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:24:13
+ |
+LL | _ => (),
+ | ^ help: try this: `Self::Rgb(..)`
+ |
+ = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:34:9
+ |
+LL | _ => {},
+ | ^ help: try this: `Foo::C`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:44:9
+ |
+LL | _ => {},
+ | ^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:52:9
+ |
+LL | _ => {},
+ | ^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:58:9
+ |
+LL | _ => {},
+ | ^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:75:9
+ |
+LL | &_ => (),
+ | ^^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:84:9
+ |
+LL | _ => (),
+ | ^ help: try this: `C::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:91:9
+ |
+LL | _ => (),
+ | ^ help: try this: `Color::Blue`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mem_forget.rs b/src/tools/clippy/tests/ui/mem_forget.rs
new file mode 100644
index 000000000..e5b35c098
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_forget.rs
@@ -0,0 +1,23 @@
+use std::rc::Rc;
+use std::sync::Arc;
+
+use std::mem as memstuff;
+use std::mem::forget as forgetSomething;
+
+#[warn(clippy::mem_forget)]
+#[allow(clippy::forget_copy)]
+fn main() {
+ let five: i32 = 5;
+ forgetSomething(five);
+
+ let six: Arc<i32> = Arc::new(6);
+ memstuff::forget(six);
+
+ let seven: Rc<i32> = Rc::new(7);
+ std::mem::forget(seven);
+
+ let eight: Vec<i32> = vec![8];
+ forgetSomething(eight);
+
+ std::mem::forget(7);
+}
diff --git a/src/tools/clippy/tests/ui/mem_forget.stderr b/src/tools/clippy/tests/ui/mem_forget.stderr
new file mode 100644
index 000000000..a90d8b165
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_forget.stderr
@@ -0,0 +1,22 @@
+error: usage of `mem::forget` on `Drop` type
+ --> $DIR/mem_forget.rs:14:5
+ |
+LL | memstuff::forget(six);
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mem-forget` implied by `-D warnings`
+
+error: usage of `mem::forget` on `Drop` type
+ --> $DIR/mem_forget.rs:17:5
+ |
+LL | std::mem::forget(seven);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: usage of `mem::forget` on `Drop` type
+ --> $DIR/mem_forget.rs:20:5
+ |
+LL | forgetSomething(eight);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed
new file mode 100644
index 000000000..b609ba659
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_replace.fixed
@@ -0,0 +1,79 @@
+// run-rustfix
+#![allow(unused_imports)]
+#![warn(
+ clippy::all,
+ clippy::style,
+ clippy::mem_replace_option_with_none,
+ clippy::mem_replace_with_default
+)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+use std::mem;
+
+fn replace_option_with_none() {
+ let mut an_option = Some(1);
+ let _ = an_option.take();
+ let an_option = &mut Some(1);
+ let _ = an_option.take();
+}
+
+fn replace_with_default() {
+ let mut s = String::from("foo");
+ let _ = std::mem::take(&mut s);
+
+ let s = &mut String::from("foo");
+ let _ = std::mem::take(s);
+ let _ = std::mem::take(s);
+
+ let mut v = vec![123];
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ let _ = std::mem::take(&mut hash_map);
+
+ let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+ let _ = std::mem::take(&mut btree_map);
+
+ let mut vd: VecDeque<i32> = VecDeque::new();
+ let _ = std::mem::take(&mut vd);
+
+ let mut hash_set: HashSet<&str> = HashSet::new();
+ let _ = std::mem::take(&mut hash_set);
+
+ let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+ let _ = std::mem::take(&mut btree_set);
+
+ let mut list: LinkedList<i32> = LinkedList::new();
+ let _ = std::mem::take(&mut list);
+
+ let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+ let _ = std::mem::take(&mut binary_heap);
+
+ let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
+ let _ = std::mem::take(&mut tuple);
+
+ let mut refstr = "hello";
+ let _ = std::mem::take(&mut refstr);
+
+ let mut slice: &[i32] = &[1, 2, 3];
+ let _ = std::mem::take(&mut slice);
+}
+
+// lint is disabled for primitives because in this case `take`
+// has no clear benefit over `replace` and sometimes is harder to read
+fn dont_lint_primitive() {
+ let mut pbool = true;
+ let _ = std::mem::replace(&mut pbool, false);
+
+ let mut pint = 5;
+ let _ = std::mem::replace(&mut pint, 0);
+}
+
+fn main() {
+ replace_option_with_none();
+ replace_with_default();
+ dont_lint_primitive();
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs
new file mode 100644
index 000000000..93f6dcdec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_replace.rs
@@ -0,0 +1,79 @@
+// run-rustfix
+#![allow(unused_imports)]
+#![warn(
+ clippy::all,
+ clippy::style,
+ clippy::mem_replace_option_with_none,
+ clippy::mem_replace_with_default
+)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+use std::mem;
+
+fn replace_option_with_none() {
+ let mut an_option = Some(1);
+ let _ = mem::replace(&mut an_option, None);
+ let an_option = &mut Some(1);
+ let _ = mem::replace(an_option, None);
+}
+
+fn replace_with_default() {
+ let mut s = String::from("foo");
+ let _ = std::mem::replace(&mut s, String::default());
+
+ let s = &mut String::from("foo");
+ let _ = std::mem::replace(s, String::default());
+ let _ = std::mem::replace(s, Default::default());
+
+ let mut v = vec![123];
+ let _ = std::mem::replace(&mut v, Vec::default());
+ let _ = std::mem::replace(&mut v, Default::default());
+ let _ = std::mem::replace(&mut v, Vec::new());
+ let _ = std::mem::replace(&mut v, vec![]);
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ let _ = std::mem::replace(&mut hash_map, HashMap::new());
+
+ let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+ let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+
+ let mut vd: VecDeque<i32> = VecDeque::new();
+ let _ = std::mem::replace(&mut vd, VecDeque::new());
+
+ let mut hash_set: HashSet<&str> = HashSet::new();
+ let _ = std::mem::replace(&mut hash_set, HashSet::new());
+
+ let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+ let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+
+ let mut list: LinkedList<i32> = LinkedList::new();
+ let _ = std::mem::replace(&mut list, LinkedList::new());
+
+ let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+ let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
+
+ let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
+ let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
+
+ let mut refstr = "hello";
+ let _ = std::mem::replace(&mut refstr, "");
+
+ let mut slice: &[i32] = &[1, 2, 3];
+ let _ = std::mem::replace(&mut slice, &[]);
+}
+
+// lint is disabled for primitives because in this case `take`
+// has no clear benefit over `replace` and sometimes is harder to read
+fn dont_lint_primitive() {
+ let mut pbool = true;
+ let _ = std::mem::replace(&mut pbool, false);
+
+ let mut pint = 5;
+ let _ = std::mem::replace(&mut pint, 0);
+}
+
+fn main() {
+ replace_option_with_none();
+ replace_with_default();
+ dont_lint_primitive();
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr
new file mode 100644
index 000000000..90dc6c95f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_replace.stderr
@@ -0,0 +1,120 @@
+error: replacing an `Option` with `None`
+ --> $DIR/mem_replace.rs:15:13
+ |
+LL | let _ = mem::replace(&mut an_option, None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
+ |
+ = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
+
+error: replacing an `Option` with `None`
+ --> $DIR/mem_replace.rs:17:13
+ |
+LL | let _ = mem::replace(an_option, None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:22:13
+ |
+LL | let _ = std::mem::replace(&mut s, String::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
+ |
+ = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:25:13
+ |
+LL | let _ = std::mem::replace(s, String::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:26:13
+ |
+LL | let _ = std::mem::replace(s, Default::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:29:13
+ |
+LL | let _ = std::mem::replace(&mut v, Vec::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:30:13
+ |
+LL | let _ = std::mem::replace(&mut v, Default::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:31:13
+ |
+LL | let _ = std::mem::replace(&mut v, Vec::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:32:13
+ |
+LL | let _ = std::mem::replace(&mut v, vec![]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:35:13
+ |
+LL | let _ = std::mem::replace(&mut hash_map, HashMap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:38:13
+ |
+LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:41:13
+ |
+LL | let _ = std::mem::replace(&mut vd, VecDeque::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:44:13
+ |
+LL | let _ = std::mem::replace(&mut hash_set, HashSet::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:47:13
+ |
+LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:50:13
+ |
+LL | let _ = std::mem::replace(&mut list, LinkedList::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:53:13
+ |
+LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:56:13
+ |
+LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:59:13
+ |
+LL | let _ = std::mem::replace(&mut refstr, "");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:62:13
+ |
+LL | let _ = std::mem::replace(&mut slice, &[]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.rs b/src/tools/clippy/tests/ui/mem_replace_macro.rs
new file mode 100644
index 000000000..0c09344b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_replace_macro.rs
@@ -0,0 +1,21 @@
+// aux-build:macro_rules.rs
+#![warn(clippy::mem_replace_with_default)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! take {
+ ($s:expr) => {
+ std::mem::replace($s, Default::default())
+ };
+}
+
+fn replace_with_default() {
+ let s = &mut String::from("foo");
+ take!(s);
+ take_external!(s);
+}
+
+fn main() {
+ replace_with_default();
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.stderr b/src/tools/clippy/tests/ui/mem_replace_macro.stderr
new file mode 100644
index 000000000..dd69ab8b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mem_replace_macro.stderr
@@ -0,0 +1,14 @@
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace_macro.rs:9:9
+ |
+LL | std::mem::replace($s, Default::default())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | take!(s);
+ | -------- in this macro invocation
+ |
+ = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
+ = note: this error originates in the macro `take` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs
new file mode 100644
index 000000000..1970c2eae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/methods.rs
@@ -0,0 +1,140 @@
+// aux-build:option_helpers.rs
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::default_trait_access,
+ clippy::missing_docs_in_private_items,
+ clippy::missing_safety_doc,
+ clippy::non_ascii_literal,
+ clippy::new_without_default,
+ clippy::needless_pass_by_value,
+ clippy::needless_lifetimes,
+ clippy::print_stdout,
+ clippy::must_use_candidate,
+ clippy::use_self,
+ clippy::useless_format,
+ clippy::wrong_self_convention,
+ clippy::unused_async,
+ clippy::unused_self,
+ unused
+)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives};
+
+struct Lt<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ #[allow(clippy::needless_lifetimes)]
+ pub fn new<'b>(s: &'b str) -> Lt<'b> {
+ unimplemented!()
+ }
+}
+
+struct Lt2<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt2<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ pub fn new(s: &str) -> Lt2 {
+ unimplemented!()
+ }
+}
+
+struct Lt3<'a> {
+ foo: &'a u32,
+}
+
+impl<'a> Lt3<'a> {
+ // The lifetime is different, but that’s irrelevant; see issue #734.
+ pub fn new() -> Lt3<'static> {
+ unimplemented!()
+ }
+}
+
+#[derive(Clone, Copy)]
+struct U;
+
+impl U {
+ fn new() -> Self {
+ U
+ }
+ // Ok because `U` is `Copy`.
+ fn to_something(self) -> u32 {
+ 0
+ }
+}
+
+struct V<T> {
+ _dummy: T,
+}
+
+impl<T> V<T> {
+ fn new() -> Option<V<T>> {
+ None
+ }
+}
+
+struct AsyncNew;
+
+impl AsyncNew {
+ async fn new() -> Option<Self> {
+ None
+ }
+}
+
+struct BadNew;
+
+impl BadNew {
+ fn new() -> i32 {
+ 0
+ }
+}
+
+struct T;
+
+impl Mul<T> for T {
+ type Output = T;
+ // No error, obviously.
+ fn mul(self, other: T) -> T {
+ self
+ }
+}
+
+/// Checks implementation of `FILTER_NEXT` lint.
+#[rustfmt::skip]
+fn filter_next() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Multi-line case.
+ let _ = v.iter().filter(|&x| {
+ *x < 0
+ }
+ ).next();
+
+ // Check that we don't lint if the caller is not an `Iterator`.
+ let foo = IteratorFalsePositives { foo: 0 };
+ let _ = foo.filter().next();
+
+ let foo = IteratorMethodFalsePositives {};
+ let _ = foo.filter(42).next();
+}
+
+fn main() {
+ filter_next();
+}
diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr
new file mode 100644
index 000000000..b63672dd6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/methods.stderr
@@ -0,0 +1,24 @@
+error: methods called `new` usually return `Self`
+ --> $DIR/methods.rs:104:5
+ |
+LL | / fn new() -> i32 {
+LL | | 0
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
+
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
+ --> $DIR/methods.rs:125:13
+ |
+LL | let _ = v.iter().filter(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).next();
+ | |___________________________^
+ |
+ = note: `-D clippy::filter-next` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed
new file mode 100644
index 000000000..ee7c1b0da
--- /dev/null
+++ b/src/tools/clippy/tests/ui/methods_fixable.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![warn(clippy::filter_next)]
+
+/// Checks implementation of `FILTER_NEXT` lint.
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Single-line case.
+ let _ = v.iter().find(|&x| *x < 0);
+}
diff --git a/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs
new file mode 100644
index 000000000..6d0f1b7bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/methods_fixable.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![warn(clippy::filter_next)]
+
+/// Checks implementation of `FILTER_NEXT` lint.
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Single-line case.
+ let _ = v.iter().filter(|&x| *x < 0).next();
+}
diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr
new file mode 100644
index 000000000..852f48e32
--- /dev/null
+++ b/src/tools/clippy/tests/ui/methods_fixable.stderr
@@ -0,0 +1,10 @@
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
+ --> $DIR/methods_fixable.rs:10:13
+ |
+LL | let _ = v.iter().filter(|&x| *x < 0).next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)`
+ |
+ = note: `-D clippy::filter-next` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs
new file mode 100644
index 000000000..b2bc97f47
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_max.rs
@@ -0,0 +1,62 @@
+#![warn(clippy::all)]
+
+use std::cmp::max as my_max;
+use std::cmp::min as my_min;
+use std::cmp::{max, min};
+
+const LARGE: usize = 3;
+
+struct NotOrd(u64);
+
+impl NotOrd {
+ fn min(self, x: u64) -> NotOrd {
+ NotOrd(x)
+ }
+
+ fn max(self, x: u64) -> NotOrd {
+ NotOrd(x)
+ }
+}
+
+fn main() {
+ let x = 2usize;
+ min(1, max(3, x));
+ min(max(3, x), 1);
+ max(min(x, 1), 3);
+ max(3, min(x, 1));
+
+ my_max(3, my_min(x, 1));
+
+ min(3, max(1, x)); // ok, could be 1, 2 or 3 depending on x
+
+ min(1, max(LARGE, x)); // no error, we don't lookup consts here
+
+ let y = 2isize;
+ min(max(y, -1), 3);
+
+ let s = "Hello";
+ min("Apple", max("Zoo", s));
+ max(min(s, "Apple"), "Zoo");
+
+ max("Apple", min(s, "Zoo")); // ok
+
+ let f = 3f32;
+ x.min(1).max(3);
+ x.max(3).min(1);
+ f.max(3f32).min(1f32);
+
+ x.max(1).min(3); // ok
+ x.min(3).max(1); // ok
+ f.min(3f32).max(1f32); // ok
+
+ max(x.min(1), 3);
+ min(x.max(1), 3); // ok
+
+ s.max("Zoo").min("Apple");
+ s.min("Apple").max("Zoo");
+
+ s.min("Zoo").max("Apple"); // ok
+
+ let not_ord = NotOrd(1);
+ not_ord.min(1).max(3); // ok
+}
diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr
new file mode 100644
index 000000000..c70b77eab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_max.stderr
@@ -0,0 +1,82 @@
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:23:5
+ |
+LL | min(1, max(3, x));
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::min-max` implied by `-D warnings`
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:24:5
+ |
+LL | min(max(3, x), 1);
+ | ^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:25:5
+ |
+LL | max(min(x, 1), 3);
+ | ^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:26:5
+ |
+LL | max(3, min(x, 1));
+ | ^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:28:5
+ |
+LL | my_max(3, my_min(x, 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:38:5
+ |
+LL | min("Apple", max("Zoo", s));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:39:5
+ |
+LL | max(min(s, "Apple"), "Zoo");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:44:5
+ |
+LL | x.min(1).max(3);
+ | ^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:45:5
+ |
+LL | x.max(3).min(1);
+ | ^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:46:5
+ |
+LL | f.max(3f32).min(1f32);
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:52:5
+ |
+LL | max(x.min(1), 3);
+ | ^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:55:5
+ |
+LL | s.max("Zoo").min("Apple");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+ --> $DIR/min_max.rs:56:5
+ |
+LL | s.min("Apple").max("Zoo");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
new file mode 100644
index 000000000..44e407bd1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -0,0 +1,228 @@
+#![allow(clippy::redundant_clone)]
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.0.0"]
+
+use std::ops::{Deref, RangeFrom};
+
+fn approx_const() {
+ let log2_10 = 3.321928094887362;
+ let log10_2 = 0.301029995663981;
+}
+
+fn cloned_instead_of_copied() {
+ let _ = [1].iter().cloned();
+}
+
+fn option_as_ref_deref() {
+ let mut opt = Some(String::from("123"));
+
+ let _ = opt.as_ref().map(String::as_str);
+ let _ = opt.as_ref().map(|x| x.as_str());
+ let _ = opt.as_mut().map(String::as_mut_str);
+ let _ = opt.as_mut().map(|x| x.as_mut_str());
+}
+
+fn match_like_matches() {
+ let _y = match Some(5) {
+ Some(0) => true,
+ _ => false,
+ };
+}
+
+fn match_same_arms() {
+ match (1, 2, 3) {
+ (1, .., 3) => 42,
+ (.., 3) => 42, //~ ERROR match arms have same body
+ _ => 0,
+ };
+}
+
+fn match_same_arms2() {
+ let _ = match Some(42) {
+ Some(_) => 24,
+ None => 24, //~ ERROR match arms have same body
+ };
+}
+
+pub fn manual_strip_msrv() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+}
+
+pub fn redundant_fieldnames() {
+ let start = 0;
+ let _ = RangeFrom { start: start };
+}
+
+pub fn redundant_static_lifetime() {
+ const VAR_ONE: &'static str = "Test constant #1";
+}
+
+pub fn checked_conversion() {
+ let value: i64 = 42;
+ let _ = value <= (u32::max_value() as i64) && value >= 0;
+ let _ = value <= (u32::MAX as i64) && value >= 0;
+}
+
+pub struct FromOverInto(String);
+
+impl Into<FromOverInto> for String {
+ fn into(self) -> FromOverInto {
+ FromOverInto(self)
+ }
+}
+
+pub fn filter_map_next() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ #[rustfmt::skip]
+ let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
+ .into_iter()
+ .filter_map(|x| {
+ if x == 2 {
+ Some(x * 2)
+ } else {
+ None
+ }
+ })
+ .next();
+}
+
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+pub fn manual_range_contains() {
+ let x = 5;
+ x >= 8 && x < 12;
+}
+
+pub fn use_self() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ fn test() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+fn replace_with_default() {
+ let mut s = String::from("foo");
+ let _ = std::mem::replace(&mut s, String::default());
+}
+
+fn map_unwrap_or() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or(_)` use.
+ // Single line case.
+ let _ = opt
+ .map(|x| x + 1)
+ // Should lint even though this call is on a separate line.
+ .unwrap_or(0);
+}
+
+// Could be const
+fn missing_const_for_fn() -> i32 {
+ 1
+}
+
+fn unnest_or_patterns() {
+ struct TS(u8, u8);
+ if let TS(0, x) | TS(1, x) = TS(0, 0) {}
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+fn deprecated_cfg_attr() {}
+
+#[warn(clippy::cast_lossless)]
+fn int_from_bool() -> u8 {
+ true as u8
+}
+
+fn err_expect() {
+ let x: Result<u32, &str> = Ok(10);
+ x.err().expect("Testing expect_err");
+}
+
+fn cast_abs_to_unsigned() {
+ let x: i32 = 10;
+ assert_eq!(10u32, x.abs() as u32);
+}
+
+fn manual_rem_euclid() {
+ let x: i32 = 10;
+ let _: i32 = ((x % 4) + 4) % 4;
+}
+
+fn main() {
+ filter_map_next();
+ checked_conversion();
+ redundant_fieldnames();
+ redundant_static_lifetime();
+ option_as_ref_deref();
+ match_like_matches();
+ match_same_arms();
+ match_same_arms2();
+ manual_strip_msrv();
+ manual_range_contains();
+ use_self();
+ replace_with_default();
+ map_unwrap_or();
+ missing_const_for_fn();
+ unnest_or_patterns();
+ int_from_bool();
+ err_expect();
+ cast_abs_to_unsigned();
+ manual_rem_euclid();
+}
+
+mod just_under_msrv {
+ #![feature(custom_inner_attributes)]
+ #![clippy::msrv = "1.44.0"]
+
+ fn main() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+ }
+}
+
+mod meets_msrv {
+ #![feature(custom_inner_attributes)]
+ #![clippy::msrv = "1.45.0"]
+
+ fn main() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+ }
+}
+
+mod just_above_msrv {
+ #![feature(custom_inner_attributes)]
+ #![clippy::msrv = "1.46.0"]
+
+ fn main() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+ }
+}
+
+mod const_rem_euclid {
+ #![feature(custom_inner_attributes)]
+ #![clippy::msrv = "1.50.0"]
+
+ pub const fn const_rem_euclid_4(num: i32) -> i32 {
+ ((num % 4) + 4) % 4
+ }
+}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
new file mode 100644
index 000000000..b1c23b539
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -0,0 +1,37 @@
+error: stripping a prefix manually
+ --> $DIR/min_rust_version_attr.rs:204:24
+ |
+LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-strip` implied by `-D warnings`
+note: the prefix was tested here
+ --> $DIR/min_rust_version_attr.rs:203:9
+ |
+LL | if s.starts_with("hello, ") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix("hello, ") {
+LL ~ assert_eq!(<stripped>.to_uppercase(), "WORLD!");
+ |
+
+error: stripping a prefix manually
+ --> $DIR/min_rust_version_attr.rs:216:24
+ |
+LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/min_rust_version_attr.rs:215:9
+ |
+LL | if s.starts_with("hello, ") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some(<stripped>) = s.strip_prefix("hello, ") {
+LL ~ assert_eq!(<stripped>.to_uppercase(), "WORLD!");
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
new file mode 100644
index 000000000..f20841891
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
@@ -0,0 +1,4 @@
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "invalid.version"]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
new file mode 100644
index 000000000..6ff88ca56
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
@@ -0,0 +1,8 @@
+error: `invalid.version` is not a valid Rust version
+ --> $DIR/min_rust_version_invalid_attr.rs:2:1
+ |
+LL | #![clippy::msrv = "invalid.version"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.rs
new file mode 100644
index 000000000..e882d5ccf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.rs
@@ -0,0 +1,11 @@
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.40"]
+#![clippy::msrv = "=1.35.0"]
+#![clippy::msrv = "1.10.1"]
+
+mod foo {
+ #![clippy::msrv = "1"]
+ #![clippy::msrv = "1.0.0"]
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.stderr
new file mode 100644
index 000000000..e3ff6605c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_multiple_inner_attr.stderr
@@ -0,0 +1,38 @@
+error: `msrv` is defined multiple times
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1
+ |
+LL | #![clippy::msrv = "=1.35.0"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first definition found here
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1
+ |
+LL | #![clippy::msrv = "1.40"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `msrv` is defined multiple times
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1
+ |
+LL | #![clippy::msrv = "1.10.1"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first definition found here
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1
+ |
+LL | #![clippy::msrv = "1.40"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `msrv` is defined multiple times
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5
+ |
+LL | #![clippy::msrv = "1.0.0"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first definition found here
+ --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5
+ |
+LL | #![clippy::msrv = "1"]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/min_rust_version_no_patch.rs b/src/tools/clippy/tests/ui/min_rust_version_no_patch.rs
new file mode 100644
index 000000000..98fffe1e3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_no_patch.rs
@@ -0,0 +1,14 @@
+#![allow(clippy::redundant_clone)]
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.0"]
+
+fn manual_strip_msrv() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+}
+
+fn main() {
+ manual_strip_msrv()
+}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_outer_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_outer_attr.rs
new file mode 100644
index 000000000..551948bd7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_outer_attr.rs
@@ -0,0 +1,4 @@
+#![feature(custom_inner_attributes)]
+
+#[clippy::msrv = "invalid.version"]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_outer_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_outer_attr.stderr
new file mode 100644
index 000000000..579ee7a87
--- /dev/null
+++ b/src/tools/clippy/tests/ui/min_rust_version_outer_attr.stderr
@@ -0,0 +1,8 @@
+error: `msrv` cannot be an outer attribute
+ --> $DIR/min_rust_version_outer_attr.rs:3:1
+ |
+LL | #[clippy::msrv = "invalid.version"]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed
new file mode 100644
index 000000000..f219a570e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#![warn(clippy::mismatched_target_os)]
+#![allow(unused)]
+
+#[cfg(target_os = "hermit")]
+fn hermit() {}
+
+#[cfg(target_os = "wasi")]
+fn wasi() {}
+
+#[cfg(target_os = "none")]
+fn none() {}
+
+// list with conditions
+#[cfg(all(not(windows), target_os = "wasi"))]
+fn list() {}
+
+// windows is a valid target family, should be ignored
+#[cfg(windows)]
+fn windows() {}
+
+// correct use, should be ignored
+#[cfg(target_os = "hermit")]
+fn correct() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs
new file mode 100644
index 000000000..8a8ae756a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+
+#![warn(clippy::mismatched_target_os)]
+#![allow(unused)]
+
+#[cfg(hermit)]
+fn hermit() {}
+
+#[cfg(wasi)]
+fn wasi() {}
+
+#[cfg(none)]
+fn none() {}
+
+// list with conditions
+#[cfg(all(not(windows), wasi))]
+fn list() {}
+
+// windows is a valid target family, should be ignored
+#[cfg(windows)]
+fn windows() {}
+
+// correct use, should be ignored
+#[cfg(target_os = "hermit")]
+fn correct() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr
new file mode 100644
index 000000000..5f1b09083
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr
@@ -0,0 +1,36 @@
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_non_unix.rs:6:1
+ |
+LL | #[cfg(hermit)]
+ | ^^^^^^------^^
+ | |
+ | help: try: `target_os = "hermit"`
+ |
+ = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_non_unix.rs:9:1
+ |
+LL | #[cfg(wasi)]
+ | ^^^^^^----^^
+ | |
+ | help: try: `target_os = "wasi"`
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_non_unix.rs:12:1
+ |
+LL | #[cfg(none)]
+ | ^^^^^^----^^
+ | |
+ | help: try: `target_os = "none"`
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_non_unix.rs:16:1
+ |
+LL | #[cfg(all(not(windows), wasi))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^----^^^
+ | |
+ | help: try: `target_os = "wasi"`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed
new file mode 100644
index 000000000..7d9d406d9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed
@@ -0,0 +1,62 @@
+// run-rustfix
+
+#![warn(clippy::mismatched_target_os)]
+#![allow(unused)]
+
+#[cfg(target_os = "linux")]
+fn linux() {}
+
+#[cfg(target_os = "freebsd")]
+fn freebsd() {}
+
+#[cfg(target_os = "dragonfly")]
+fn dragonfly() {}
+
+#[cfg(target_os = "openbsd")]
+fn openbsd() {}
+
+#[cfg(target_os = "netbsd")]
+fn netbsd() {}
+
+#[cfg(target_os = "macos")]
+fn macos() {}
+
+#[cfg(target_os = "ios")]
+fn ios() {}
+
+#[cfg(target_os = "android")]
+fn android() {}
+
+#[cfg(target_os = "emscripten")]
+fn emscripten() {}
+
+#[cfg(target_os = "fuchsia")]
+fn fuchsia() {}
+
+#[cfg(target_os = "haiku")]
+fn haiku() {}
+
+#[cfg(target_os = "illumos")]
+fn illumos() {}
+
+#[cfg(target_os = "l4re")]
+fn l4re() {}
+
+#[cfg(target_os = "redox")]
+fn redox() {}
+
+#[cfg(target_os = "solaris")]
+fn solaris() {}
+
+#[cfg(target_os = "vxworks")]
+fn vxworks() {}
+
+// list with conditions
+#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))]
+fn list() {}
+
+// correct use, should be ignored
+#[cfg(target_os = "freebsd")]
+fn correct() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs
new file mode 100644
index 000000000..c1177f1ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs
@@ -0,0 +1,62 @@
+// run-rustfix
+
+#![warn(clippy::mismatched_target_os)]
+#![allow(unused)]
+
+#[cfg(linux)]
+fn linux() {}
+
+#[cfg(freebsd)]
+fn freebsd() {}
+
+#[cfg(dragonfly)]
+fn dragonfly() {}
+
+#[cfg(openbsd)]
+fn openbsd() {}
+
+#[cfg(netbsd)]
+fn netbsd() {}
+
+#[cfg(macos)]
+fn macos() {}
+
+#[cfg(ios)]
+fn ios() {}
+
+#[cfg(android)]
+fn android() {}
+
+#[cfg(emscripten)]
+fn emscripten() {}
+
+#[cfg(fuchsia)]
+fn fuchsia() {}
+
+#[cfg(haiku)]
+fn haiku() {}
+
+#[cfg(illumos)]
+fn illumos() {}
+
+#[cfg(l4re)]
+fn l4re() {}
+
+#[cfg(redox)]
+fn redox() {}
+
+#[cfg(solaris)]
+fn solaris() {}
+
+#[cfg(vxworks)]
+fn vxworks() {}
+
+// list with conditions
+#[cfg(all(not(any(solaris, linux)), freebsd))]
+fn list() {}
+
+// correct use, should be ignored
+#[cfg(target_os = "freebsd")]
+fn correct() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr
new file mode 100644
index 000000000..3534b5328
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr
@@ -0,0 +1,183 @@
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:6:1
+ |
+LL | #[cfg(linux)]
+ | ^^^^^^-----^^
+ | |
+ | help: try: `target_os = "linux"`
+ |
+ = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:9:1
+ |
+LL | #[cfg(freebsd)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "freebsd"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:12:1
+ |
+LL | #[cfg(dragonfly)]
+ | ^^^^^^---------^^
+ | |
+ | help: try: `target_os = "dragonfly"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:15:1
+ |
+LL | #[cfg(openbsd)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "openbsd"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:18:1
+ |
+LL | #[cfg(netbsd)]
+ | ^^^^^^------^^
+ | |
+ | help: try: `target_os = "netbsd"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:21:1
+ |
+LL | #[cfg(macos)]
+ | ^^^^^^-----^^
+ | |
+ | help: try: `target_os = "macos"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:24:1
+ |
+LL | #[cfg(ios)]
+ | ^^^^^^---^^
+ | |
+ | help: try: `target_os = "ios"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:27:1
+ |
+LL | #[cfg(android)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "android"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:30:1
+ |
+LL | #[cfg(emscripten)]
+ | ^^^^^^----------^^
+ | |
+ | help: try: `target_os = "emscripten"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:33:1
+ |
+LL | #[cfg(fuchsia)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "fuchsia"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:36:1
+ |
+LL | #[cfg(haiku)]
+ | ^^^^^^-----^^
+ | |
+ | help: try: `target_os = "haiku"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:39:1
+ |
+LL | #[cfg(illumos)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "illumos"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:42:1
+ |
+LL | #[cfg(l4re)]
+ | ^^^^^^----^^
+ | |
+ | help: try: `target_os = "l4re"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:45:1
+ |
+LL | #[cfg(redox)]
+ | ^^^^^^-----^^
+ | |
+ | help: try: `target_os = "redox"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:48:1
+ |
+LL | #[cfg(solaris)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "solaris"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:51:1
+ |
+LL | #[cfg(vxworks)]
+ | ^^^^^^-------^^
+ | |
+ | help: try: `target_os = "vxworks"`
+ |
+ = help: did you mean `unix`?
+
+error: operating system used in target family position
+ --> $DIR/mismatched_target_os_unix.rs:55:1
+ |
+LL | #[cfg(all(not(any(solaris, linux)), freebsd))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: did you mean `unix`?
+help: try
+ |
+LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))]
+ | ~~~~~~~~~~~~~~~~~~~~~
+help: try
+ |
+LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))]
+ | ~~~~~~~~~~~~~~~~~~~
+help: try
+ |
+LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))]
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs
new file mode 100644
index 000000000..8c0da84d8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs
@@ -0,0 +1,64 @@
+#![warn(clippy::mismatching_type_param_order)]
+#![allow(clippy::blacklisted_name)]
+
+fn main() {
+ struct Foo<A, B> {
+ x: A,
+ y: B,
+ }
+
+ // lint on both params
+ impl<B, A> Foo<B, A> {}
+
+ // lint on the 2nd param
+ impl<C, A> Foo<C, A> {}
+
+ // should not lint
+ impl<A, B> Foo<A, B> {}
+
+ struct FooLifetime<'l, 'm, A, B> {
+ x: &'l A,
+ y: &'m B,
+ }
+
+ // should not lint on lifetimes
+ impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
+
+ struct Bar {
+ x: i32,
+ }
+
+ // should not lint
+ impl Bar {}
+
+ // also works for enums
+ enum FooEnum<A, B, C> {
+ X(A),
+ Y(B),
+ Z(C),
+ }
+
+ impl<C, A, B> FooEnum<C, A, B> {}
+
+ // also works for unions
+ union FooUnion<A: Copy, B>
+ where
+ B: Copy,
+ {
+ x: A,
+ y: B,
+ }
+
+ impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
+
+ impl<A, B> FooUnion<A, B>
+ where
+ A: Copy,
+ B: Copy,
+ {
+ }
+
+ // if the types are complicated, do not lint
+ impl<K, V, B> Foo<(K, V), B> {}
+ impl<K, V, A> Foo<(K, V), A> {}
+}
diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr
new file mode 100644
index 000000000..cb720256c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr
@@ -0,0 +1,83 @@
+error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:11:20
+ |
+LL | impl<B, A> Foo<B, A> {}
+ | ^
+ |
+ = note: `-D clippy::mismatching-type-param-order` implied by `-D warnings`
+ = help: try `A`, or a name that does not conflict with `Foo`'s generic params
+
+error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:11:23
+ |
+LL | impl<B, A> Foo<B, A> {}
+ | ^
+ |
+ = help: try `B`, or a name that does not conflict with `Foo`'s generic params
+
+error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:14:23
+ |
+LL | impl<C, A> Foo<C, A> {}
+ | ^
+ |
+ = help: try `B`, or a name that does not conflict with `Foo`'s generic params
+
+error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:25:44
+ |
+LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
+ | ^
+ |
+ = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params
+
+error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:25:47
+ |
+LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
+ | ^
+ |
+ = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params
+
+error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:41:27
+ |
+LL | impl<C, A, B> FooEnum<C, A, B> {}
+ | ^
+ |
+ = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params
+
+error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:41:30
+ |
+LL | impl<C, A, B> FooEnum<C, A, B> {}
+ | ^
+ |
+ = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params
+
+error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:41:33
+ |
+LL | impl<C, A, B> FooEnum<C, A, B> {}
+ | ^
+ |
+ = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params
+
+error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:52:31
+ |
+LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
+ | ^
+ |
+ = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params
+
+error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order
+ --> $DIR/mismatching_type_param_order.rs:52:34
+ |
+LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
+ | ^
+ |
+ = help: try `B`, or a name that does not conflict with `FooUnion`'s generic params
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs
new file mode 100644
index 000000000..51fd57df8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs
@@ -0,0 +1,3 @@
+#![warn(clippy::missing_docs_in_private_items)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
new file mode 100644
index 000000000..d56c5cc4c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
@@ -0,0 +1,12 @@
+error: missing documentation for the crate
+ --> $DIR/missing-doc-crate-missing.rs:1:1
+ |
+LL | / #![warn(clippy::missing_docs_in_private_items)]
+LL | |
+LL | | fn main() {}
+ | |____________^
+ |
+ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.rs b/src/tools/clippy/tests/ui/missing-doc-crate.rs
new file mode 100644
index 000000000..e00c7fbfe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc-crate.rs
@@ -0,0 +1,4 @@
+#![warn(clippy::missing_docs_in_private_items)]
+#![doc = include_str!("../../README.md")]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.rs b/src/tools/clippy/tests/ui/missing-doc-impl.rs
new file mode 100644
index 000000000..d5724bf66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc-impl.rs
@@ -0,0 +1,92 @@
+#![warn(clippy::missing_docs_in_private_items)]
+#![allow(dead_code)]
+#![feature(associated_type_defaults)]
+
+//! Some garbage docs for the crate here
+#![doc = "More garbage"]
+
+struct Foo {
+ a: isize,
+ b: isize,
+}
+
+pub struct PubFoo {
+ pub a: isize,
+ b: isize,
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+pub struct PubFoo2 {
+ pub a: isize,
+ pub c: isize,
+}
+
+/// dox
+pub trait A {
+ /// dox
+ fn foo(&self);
+ /// dox
+ fn foo_with_impl(&self) {}
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+trait B {
+ fn foo(&self);
+ fn foo_with_impl(&self) {}
+}
+
+pub trait C {
+ fn foo(&self);
+ fn foo_with_impl(&self) {}
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+pub trait D {
+ fn dummy(&self) {}
+}
+
+/// dox
+pub trait E: Sized {
+ type AssociatedType;
+ type AssociatedTypeDef = Self;
+
+ /// dox
+ type DocumentedType;
+ /// dox
+ type DocumentedTypeDef = Self;
+ /// dox
+ fn dummy(&self) {}
+}
+
+impl Foo {
+ pub fn new() -> Self {
+ Foo { a: 0, b: 0 }
+ }
+ fn bar() {}
+}
+
+impl PubFoo {
+ pub fn foo() {}
+ /// dox
+ pub fn foo1() {}
+ #[must_use = "yep"]
+ fn foo2() -> u32 {
+ 1
+ }
+ #[allow(clippy::missing_docs_in_private_items)]
+ pub fn foo3() {}
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+trait F {
+ fn a();
+ fn b(&self);
+}
+
+// should need to redefine documentation for implementations of traits
+impl F for Foo {
+ fn a() {}
+ fn b(&self) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.stderr b/src/tools/clippy/tests/ui/missing-doc-impl.stderr
new file mode 100644
index 000000000..bda63d66a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc-impl.stderr
@@ -0,0 +1,107 @@
+error: missing documentation for a struct
+ --> $DIR/missing-doc-impl.rs:8:1
+ |
+LL | / struct Foo {
+LL | | a: isize,
+LL | | b: isize,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc-impl.rs:9:5
+ |
+LL | a: isize,
+ | ^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc-impl.rs:10:5
+ |
+LL | b: isize,
+ | ^^^^^^^^
+
+error: missing documentation for a struct
+ --> $DIR/missing-doc-impl.rs:13:1
+ |
+LL | / pub struct PubFoo {
+LL | | pub a: isize,
+LL | | b: isize,
+LL | | }
+ | |_^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc-impl.rs:14:5
+ |
+LL | pub a: isize,
+ | ^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc-impl.rs:15:5
+ |
+LL | b: isize,
+ | ^^^^^^^^
+
+error: missing documentation for a trait
+ --> $DIR/missing-doc-impl.rs:38:1
+ |
+LL | / pub trait C {
+LL | | fn foo(&self);
+LL | | fn foo_with_impl(&self) {}
+LL | | }
+ | |_^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:39:5
+ |
+LL | fn foo(&self);
+ | ^^^^^^^^^^^^^^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:40:5
+ |
+LL | fn foo_with_impl(&self) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated type
+ --> $DIR/missing-doc-impl.rs:50:5
+ |
+LL | type AssociatedType;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated type
+ --> $DIR/missing-doc-impl.rs:51:5
+ |
+LL | type AssociatedTypeDef = Self;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:62:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Foo { a: 0, b: 0 }
+LL | | }
+ | |_____^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:65:5
+ |
+LL | fn bar() {}
+ | ^^^^^^^^^^^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:69:5
+ |
+LL | pub fn foo() {}
+ | ^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated function
+ --> $DIR/missing-doc-impl.rs:73:5
+ |
+LL | / fn foo2() -> u32 {
+LL | | 1
+LL | | }
+ | |_____^
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing-doc.rs b/src/tools/clippy/tests/ui/missing-doc.rs
new file mode 100644
index 000000000..6e2e710e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc.rs
@@ -0,0 +1,102 @@
+#![warn(clippy::missing_docs_in_private_items)]
+// When denying at the crate level, be sure to not get random warnings from the
+// injected intrinsics by the compiler.
+#![allow(dead_code)]
+//! Some garbage docs for the crate here
+#![doc = "More garbage"]
+
+use std::arch::global_asm;
+
+type Typedef = String;
+pub type PubTypedef = String;
+
+mod module_no_dox {}
+pub mod pub_module_no_dox {}
+
+/// dox
+pub fn foo() {}
+pub fn foo2() {}
+fn foo3() {}
+#[allow(clippy::missing_docs_in_private_items)]
+pub fn foo4() {}
+
+// It sure is nice if doc(hidden) implies allow(missing_docs), and that it
+// applies recursively
+#[doc(hidden)]
+mod a {
+ pub fn baz() {}
+ pub mod b {
+ pub fn baz() {}
+ }
+}
+
+enum Baz {
+ BazA { a: isize, b: isize },
+ BarB,
+}
+
+pub enum PubBaz {
+ PubBazA { a: isize },
+}
+
+/// dox
+pub enum PubBaz2 {
+ /// dox
+ PubBaz2A {
+ /// dox
+ a: isize,
+ },
+}
+
+#[allow(clippy::missing_docs_in_private_items)]
+pub enum PubBaz3 {
+ PubBaz3A { b: isize },
+}
+
+#[doc(hidden)]
+pub fn baz() {}
+
+const FOO: u32 = 0;
+/// dox
+pub const FOO1: u32 = 0;
+#[allow(clippy::missing_docs_in_private_items)]
+pub const FOO2: u32 = 0;
+#[doc(hidden)]
+pub const FOO3: u32 = 0;
+pub const FOO4: u32 = 0;
+
+static BAR: u32 = 0;
+/// dox
+pub static BAR1: u32 = 0;
+#[allow(clippy::missing_docs_in_private_items)]
+pub static BAR2: u32 = 0;
+#[doc(hidden)]
+pub static BAR3: u32 = 0;
+pub static BAR4: u32 = 0;
+
+mod internal_impl {
+ /// dox
+ pub fn documented() {}
+ pub fn undocumented1() {}
+ pub fn undocumented2() {}
+ fn undocumented3() {}
+ /// dox
+ pub mod globbed {
+ /// dox
+ pub fn also_documented() {}
+ pub fn also_undocumented1() {}
+ fn also_undocumented2() {}
+ }
+}
+/// dox
+pub mod public_interface {
+ pub use crate::internal_impl::documented as foo;
+ pub use crate::internal_impl::globbed::*;
+ pub use crate::internal_impl::undocumented1 as bar;
+ pub use crate::internal_impl::{documented, undocumented2};
+}
+
+fn main() {}
+
+// Ensure global asm doesn't require documentation.
+global_asm! { "" }
diff --git a/src/tools/clippy/tests/ui/missing-doc.stderr b/src/tools/clippy/tests/ui/missing-doc.stderr
new file mode 100644
index 000000000..a876dc078
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing-doc.stderr
@@ -0,0 +1,159 @@
+error: missing documentation for a type alias
+ --> $DIR/missing-doc.rs:10:1
+ |
+LL | type Typedef = String;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
+
+error: missing documentation for a type alias
+ --> $DIR/missing-doc.rs:11:1
+ |
+LL | pub type PubTypedef = String;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
+ --> $DIR/missing-doc.rs:13:1
+ |
+LL | mod module_no_dox {}
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
+ --> $DIR/missing-doc.rs:14:1
+ |
+LL | pub mod pub_module_no_dox {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:18:1
+ |
+LL | pub fn foo2() {}
+ | ^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:19:1
+ |
+LL | fn foo3() {}
+ | ^^^^^^^^^^^^
+
+error: missing documentation for an enum
+ --> $DIR/missing-doc.rs:33:1
+ |
+LL | / enum Baz {
+LL | | BazA { a: isize, b: isize },
+LL | | BarB,
+LL | | }
+ | |_^
+
+error: missing documentation for a variant
+ --> $DIR/missing-doc.rs:34:5
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc.rs:34:12
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc.rs:34:22
+ |
+LL | BazA { a: isize, b: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a variant
+ --> $DIR/missing-doc.rs:35:5
+ |
+LL | BarB,
+ | ^^^^
+
+error: missing documentation for an enum
+ --> $DIR/missing-doc.rs:38:1
+ |
+LL | / pub enum PubBaz {
+LL | | PubBazA { a: isize },
+LL | | }
+ | |_^
+
+error: missing documentation for a variant
+ --> $DIR/missing-doc.rs:39:5
+ |
+LL | PubBazA { a: isize },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/missing-doc.rs:39:15
+ |
+LL | PubBazA { a: isize },
+ | ^^^^^^^^
+
+error: missing documentation for a constant
+ --> $DIR/missing-doc.rs:59:1
+ |
+LL | const FOO: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a constant
+ --> $DIR/missing-doc.rs:66:1
+ |
+LL | pub const FOO4: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a static
+ --> $DIR/missing-doc.rs:68:1
+ |
+LL | static BAR: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a static
+ --> $DIR/missing-doc.rs:75:1
+ |
+LL | pub static BAR4: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a module
+ --> $DIR/missing-doc.rs:77:1
+ |
+LL | / mod internal_impl {
+LL | | /// dox
+LL | | pub fn documented() {}
+LL | | pub fn undocumented1() {}
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:80:5
+ |
+LL | pub fn undocumented1() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:81:5
+ |
+LL | pub fn undocumented2() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:82:5
+ |
+LL | fn undocumented3() {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:87:9
+ |
+LL | pub fn also_undocumented1() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/missing-doc.rs:88:9
+ |
+LL | fn also_undocumented2() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
new file mode 100644
index 000000000..7b9dc76b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
@@ -0,0 +1,8 @@
+// This file provides a const function that is unstably const forever.
+
+#![feature(staged_api)]
+#![stable(feature = "1", since = "1.0.0")]
+
+#[stable(feature = "1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "foo", issue = "none")]
+pub const fn unstably_const_fn() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
new file mode 100644
index 000000000..aa60d0504
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -0,0 +1,121 @@
+//! False-positive tests to ensure we don't suggest `const` for things where it would cause a
+//! compilation error.
+//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
+
+// aux-build:helper.rs
+
+#![warn(clippy::missing_const_for_fn)]
+#![feature(start)]
+#![feature(custom_inner_attributes)]
+
+extern crate helper;
+
+struct Game;
+
+// This should not be linted because it's already const
+const fn already_const() -> i32 {
+ 32
+}
+
+impl Game {
+ // This should not be linted because it's already const
+ pub const fn already_const() -> i32 {
+ 32
+ }
+}
+
+// Allowing on this function, because it would lint, which we don't want in this case.
+#[allow(clippy::missing_const_for_fn)]
+fn random() -> u32 {
+ 42
+}
+
+// We should not suggest to make this function `const` because `random()` is non-const
+fn random_caller() -> u32 {
+ random()
+}
+
+static Y: u32 = 0;
+
+// We should not suggest to make this function `const` because const functions are not allowed to
+// refer to a static variable
+fn get_y() -> u32 {
+ Y
+ //~^ ERROR E0013
+}
+
+// Don't lint entrypoint functions
+#[start]
+fn init(num: isize, something: *const *const u8) -> isize {
+ 1
+}
+
+trait Foo {
+ // This should not be suggested to be made const
+ // (rustc doesn't allow const trait methods)
+ fn f() -> u32;
+
+ // This should not be suggested to be made const either
+ fn g() -> u32 {
+ 33
+ }
+}
+
+// Don't lint in external macros (derive)
+#[derive(PartialEq, Eq)]
+struct Point(isize, isize);
+
+impl std::ops::Add for Point {
+ type Output = Self;
+
+ // Don't lint in trait impls of derived methods
+ fn add(self, other: Self) -> Self {
+ Point(self.0 + other.0, self.1 + other.1)
+ }
+}
+
+mod with_drop {
+ pub struct A;
+ pub struct B;
+ impl Drop for A {
+ fn drop(&mut self) {}
+ }
+
+ impl A {
+ // This can not be const because the type implements `Drop`.
+ pub fn b(self) -> B {
+ B
+ }
+ }
+
+ impl B {
+ // This can not be const because `a` implements `Drop`.
+ pub fn a(self, a: A) -> B {
+ B
+ }
+ }
+}
+
+fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] {
+ t
+}
+
+fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
+ let p = t.as_ptr() as *const [T; N];
+
+ unsafe { &*p }
+}
+
+// Do not lint this because it calls a function whose constness is unstable.
+fn unstably_const_fn() {
+ helper::unstably_const_fn()
+}
+
+mod const_fn_stabilized_after_msrv {
+ #![clippy::msrv = "1.46.0"]
+
+ // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
+ fn const_fn_stabilized_after_msrv(byte: u8) {
+ byte.is_ascii_digit();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
new file mode 100644
index 000000000..88f6935d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -0,0 +1,81 @@
+#![warn(clippy::missing_const_for_fn)]
+#![allow(incomplete_features, clippy::let_and_return)]
+#![feature(custom_inner_attributes)]
+
+use std::mem::transmute;
+
+struct Game {
+ guess: i32,
+}
+
+impl Game {
+ // Could be const
+ pub fn new() -> Self {
+ Self { guess: 42 }
+ }
+
+ fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
+ b
+ }
+}
+
+// Could be const
+fn one() -> i32 {
+ 1
+}
+
+// Could also be const
+fn two() -> i32 {
+ let abc = 2;
+ abc
+}
+
+// Could be const (since Rust 1.39)
+fn string() -> String {
+ String::new()
+}
+
+// Could be const
+unsafe fn four() -> i32 {
+ 4
+}
+
+// Could also be const
+fn generic<T>(t: T) -> T {
+ t
+}
+
+fn sub(x: u32) -> usize {
+ unsafe { transmute(&x) }
+}
+
+fn generic_arr<T: Copy>(t: [T; 1]) -> T {
+ t[0]
+}
+
+mod with_drop {
+ pub struct A;
+ pub struct B;
+ impl Drop for A {
+ fn drop(&mut self) {}
+ }
+
+ impl B {
+ // This can be const, because `a` is passed by reference
+ pub fn b(self, a: &A) -> B {
+ B
+ }
+ }
+}
+
+mod const_fn_stabilized_before_msrv {
+ #![clippy::msrv = "1.47.0"]
+
+ // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
+ fn const_fn_stabilized_before_msrv(byte: u8) {
+ byte.is_ascii_digit();
+ }
+}
+
+// Should not be const
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
new file mode 100644
index 000000000..3eb52b682
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -0,0 +1,85 @@
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:13:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Self { guess: 42 }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:17:5
+ |
+LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
+LL | | b
+LL | | }
+ | |_____^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:23:1
+ |
+LL | / fn one() -> i32 {
+LL | | 1
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:28:1
+ |
+LL | / fn two() -> i32 {
+LL | | let abc = 2;
+LL | | abc
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:34:1
+ |
+LL | / fn string() -> String {
+LL | | String::new()
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:39:1
+ |
+LL | / unsafe fn four() -> i32 {
+LL | | 4
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:44:1
+ |
+LL | / fn generic<T>(t: T) -> T {
+LL | | t
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:52:1
+ |
+LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
+LL | | t[0]
+LL | | }
+ | |_^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:65:9
+ |
+LL | / pub fn b(self, a: &A) -> B {
+LL | | B
+LL | | }
+ | |_________^
+
+error: this could be a `const fn`
+ --> $DIR/could_be_const.rs:75:5
+ |
+LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
+LL | | byte.is_ascii_digit();
+LL | | }
+ | |_____^
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs
new file mode 100644
index 000000000..07f8e3888
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_inline.rs
@@ -0,0 +1,66 @@
+#![warn(clippy::missing_inline_in_public_items)]
+#![crate_type = "dylib"]
+// When denying at the crate level, be sure to not get random warnings from the
+// injected intrinsics by the compiler.
+#![allow(dead_code, non_snake_case)]
+
+type Typedef = String;
+pub type PubTypedef = String;
+
+struct Foo; // ok
+pub struct PubFoo; // ok
+enum FooE {} // ok
+pub enum PubFooE {} // ok
+
+mod module {} // ok
+pub mod pub_module {} // ok
+
+fn foo() {}
+pub fn pub_foo() {} // missing #[inline]
+#[inline]
+pub fn pub_foo_inline() {} // ok
+#[inline(always)]
+pub fn pub_foo_inline_always() {} // ok
+
+#[allow(clippy::missing_inline_in_public_items)]
+pub fn pub_foo_no_inline() {}
+
+trait Bar {
+ fn Bar_a(); // ok
+ fn Bar_b() {} // ok
+}
+
+pub trait PubBar {
+ fn PubBar_a(); // ok
+ fn PubBar_b() {} // missing #[inline]
+ #[inline]
+ fn PubBar_c() {} // ok
+}
+
+// none of these need inline because Foo is not exported
+impl PubBar for Foo {
+ fn PubBar_a() {} // ok
+ fn PubBar_b() {} // ok
+ fn PubBar_c() {} // ok
+}
+
+// all of these need inline because PubFoo is exported
+impl PubBar for PubFoo {
+ fn PubBar_a() {} // missing #[inline]
+ fn PubBar_b() {} // missing #[inline]
+ fn PubBar_c() {} // missing #[inline]
+}
+
+// do not need inline because Foo is not exported
+impl Foo {
+ fn FooImpl() {} // ok
+}
+
+// need inline because PubFoo is exported
+impl PubFoo {
+ pub fn PubFooImpl() {} // missing #[inline]
+}
+
+// do not lint this since users cannot control the external code
+#[derive(Debug)]
+pub struct S;
diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr
new file mode 100644
index 000000000..40b92b764
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_inline.stderr
@@ -0,0 +1,40 @@
+error: missing `#[inline]` for a function
+ --> $DIR/missing_inline.rs:19:1
+ |
+LL | pub fn pub_foo() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings`
+
+error: missing `#[inline]` for a default trait method
+ --> $DIR/missing_inline.rs:35:5
+ |
+LL | fn PubBar_b() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^
+
+error: missing `#[inline]` for a method
+ --> $DIR/missing_inline.rs:49:5
+ |
+LL | fn PubBar_a() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^
+
+error: missing `#[inline]` for a method
+ --> $DIR/missing_inline.rs:50:5
+ |
+LL | fn PubBar_b() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^
+
+error: missing `#[inline]` for a method
+ --> $DIR/missing_inline.rs:51:5
+ |
+LL | fn PubBar_c() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^
+
+error: missing `#[inline]` for a method
+ --> $DIR/missing_inline.rs:61:5
+ |
+LL | pub fn PubFooImpl() {} // missing #[inline]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.rs b/src/tools/clippy/tests/ui/missing_inline_executable.rs
new file mode 100644
index 000000000..6e0400ac9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_inline_executable.rs
@@ -0,0 +1,5 @@
+#![warn(clippy::missing_inline_in_public_items)]
+
+pub fn foo() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs
new file mode 100644
index 000000000..3c68fb905
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::missing_inline_in_public_items)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+fn _foo() {}
+
+#[proc_macro]
+pub fn function_like(_: TokenStream) -> TokenStream {
+ TokenStream::new()
+}
+
+#[proc_macro_attribute]
+pub fn attribute(_: TokenStream, _: TokenStream) -> TokenStream {
+ TokenStream::new()
+}
+
+#[proc_macro_derive(Derive)]
+pub fn derive(_: TokenStream) -> TokenStream {
+ TokenStream::new()
+}
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs
new file mode 100644
index 000000000..7dc445292
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs
@@ -0,0 +1,153 @@
+#![warn(clippy::missing_panics_doc)]
+#![allow(clippy::option_map_unit_fn)]
+fn main() {}
+
+/// This needs to be documented
+pub fn unwrap() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This needs to be documented
+pub fn panic() {
+ panic!("This function panics")
+}
+
+/// This needs to be documented
+pub fn todo() {
+ todo!()
+}
+
+/// This needs to be documented
+pub fn inner_body(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This needs to be documented
+pub fn unreachable_and_panic() {
+ if true { unreachable!() } else { panic!() }
+}
+
+/// This needs to be documented
+pub fn assert_eq() {
+ let x = 0;
+ assert_eq!(x, 0);
+}
+
+/// This needs to be documented
+pub fn assert_ne() {
+ let x = 0;
+ assert_ne!(x, 0);
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `result` if an error
+pub fn unwrap_documented() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics just because
+pub fn panic_documented() {
+ panic!("This function panics")
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `opt` is Just(10)
+pub fn inner_body_documented(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// We still need to do this part
+pub fn todo_documented() {
+ todo!()
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// We still need to do this part
+pub fn unreachable_amd_panic_documented() {
+ if true { unreachable!() } else { panic!() }
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `x` is not 0.
+pub fn assert_eq_documented() {
+ let x = 0;
+ assert_eq!(x, 0);
+}
+
+/// This is documented
+///
+/// # Panics
+///
+/// Panics if `x` is 0.
+pub fn assert_ne_documented() {
+ let x = 0;
+ assert_ne!(x, 0);
+}
+
+/// This is okay because it is private
+fn unwrap_private() {
+ let result = Err("Hi");
+ result.unwrap()
+}
+
+/// This is okay because it is private
+fn panic_private() {
+ panic!("This function panics")
+}
+
+/// This is okay because it is private
+fn todo_private() {
+ todo!()
+}
+
+/// This is okay because it is private
+fn inner_body_private(opt: Option<u32>) {
+ opt.map(|x| {
+ if x == 10 {
+ panic!()
+ }
+ });
+}
+
+/// This is okay because unreachable
+pub fn unreachable() {
+ unreachable!("This function panics")
+}
+
+/// #6970.
+/// This is okay because it is expansion of `debug_assert` family.
+pub fn debug_assertions() {
+ debug_assert!(false);
+ debug_assert_eq!(1, 2);
+ debug_assert_ne!(1, 2);
+}
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
new file mode 100644
index 000000000..91ebd6952
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
@@ -0,0 +1,108 @@
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:6:1
+ |
+LL | / pub fn unwrap() {
+LL | | let result = Err("Hi");
+LL | | result.unwrap()
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::missing-panics-doc` implied by `-D warnings`
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:8:5
+ |
+LL | result.unwrap()
+ | ^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:12:1
+ |
+LL | / pub fn panic() {
+LL | | panic!("This function panics")
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:13:5
+ |
+LL | panic!("This function panics")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:17:1
+ |
+LL | / pub fn todo() {
+LL | | todo!()
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:18:5
+ |
+LL | todo!()
+ | ^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:22:1
+ |
+LL | / pub fn inner_body(opt: Option<u32>) {
+LL | | opt.map(|x| {
+LL | | if x == 10 {
+LL | | panic!()
+LL | | }
+LL | | });
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:25:13
+ |
+LL | panic!()
+ | ^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:31:1
+ |
+LL | / pub fn unreachable_and_panic() {
+LL | | if true { unreachable!() } else { panic!() }
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:32:39
+ |
+LL | if true { unreachable!() } else { panic!() }
+ | ^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:36:1
+ |
+LL | / pub fn assert_eq() {
+LL | | let x = 0;
+LL | | assert_eq!(x, 0);
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:38:5
+ |
+LL | assert_eq!(x, 0);
+ | ^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+ --> $DIR/missing_panics_doc.rs:42:1
+ |
+LL | / pub fn assert_ne() {
+LL | | let x = 0;
+LL | | assert_ne!(x, 0);
+LL | | }
+ | |_^
+ |
+note: first possible panic found here
+ --> $DIR/missing_panics_doc.rs:44:5
+ |
+LL | assert_ne!(x, 0);
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.fixed b/src/tools/clippy/tests/ui/missing_spin_loop.fixed
new file mode 100644
index 000000000..aa89e04d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop.fixed
@@ -0,0 +1,28 @@
+// run-rustfix
+#![warn(clippy::missing_spin_loop)]
+#![allow(clippy::bool_comparison)]
+#![allow(unused_braces)]
+
+use core::sync::atomic::{AtomicBool, Ordering};
+
+fn main() {
+ let b = AtomicBool::new(true);
+ // Those should lint
+ while b.load(Ordering::Acquire) { std::hint::spin_loop() }
+
+ while !b.load(Ordering::SeqCst) { std::hint::spin_loop() }
+
+ while b.load(Ordering::Acquire) == false { std::hint::spin_loop() }
+
+ while { true == b.load(Ordering::Acquire) } { std::hint::spin_loop() }
+
+ while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) { std::hint::spin_loop() }
+
+ while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) { std::hint::spin_loop() }
+
+ // This is OK, as the body is not empty
+ while b.load(Ordering::Acquire) {
+ std::hint::spin_loop()
+ }
+ // TODO: also match on loop+match or while let
+}
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.rs b/src/tools/clippy/tests/ui/missing_spin_loop.rs
new file mode 100644
index 000000000..88745e477
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop.rs
@@ -0,0 +1,28 @@
+// run-rustfix
+#![warn(clippy::missing_spin_loop)]
+#![allow(clippy::bool_comparison)]
+#![allow(unused_braces)]
+
+use core::sync::atomic::{AtomicBool, Ordering};
+
+fn main() {
+ let b = AtomicBool::new(true);
+ // Those should lint
+ while b.load(Ordering::Acquire) {}
+
+ while !b.load(Ordering::SeqCst) {}
+
+ while b.load(Ordering::Acquire) == false {}
+
+ while { true == b.load(Ordering::Acquire) } {}
+
+ while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {}
+
+ while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {}
+
+ // This is OK, as the body is not empty
+ while b.load(Ordering::Acquire) {
+ std::hint::spin_loop()
+ }
+ // TODO: also match on loop+match or while let
+}
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.stderr b/src/tools/clippy/tests/ui/missing_spin_loop.stderr
new file mode 100644
index 000000000..485da00dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop.stderr
@@ -0,0 +1,40 @@
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:11:37
+ |
+LL | while b.load(Ordering::Acquire) {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+ |
+ = note: `-D clippy::missing-spin-loop` implied by `-D warnings`
+
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:13:37
+ |
+LL | while !b.load(Ordering::SeqCst) {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:15:46
+ |
+LL | while b.load(Ordering::Acquire) == false {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:17:49
+ |
+LL | while { true == b.load(Ordering::Acquire) } {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:19:93
+ |
+LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop.rs:21:94
+ |
+LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {}
+ | ^^ help: try this: `{ std::hint::spin_loop() }`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop_no_std.fixed b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.fixed
new file mode 100644
index 000000000..bb4b47955
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+#![warn(clippy::missing_spin_loop)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+use core::sync::atomic::{AtomicBool, Ordering};
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ // This should trigger the lint
+ let b = AtomicBool::new(true);
+ // This should lint with `core::hint::spin_loop()`
+ while b.load(Ordering::Acquire) { core::hint::spin_loop() }
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop_no_std.rs b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.rs
new file mode 100644
index 000000000..a19bc72ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+#![warn(clippy::missing_spin_loop)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+use core::sync::atomic::{AtomicBool, Ordering};
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ // This should trigger the lint
+ let b = AtomicBool::new(true);
+ // This should lint with `core::hint::spin_loop()`
+ while b.load(Ordering::Acquire) {}
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/src/tools/clippy/tests/ui/missing_spin_loop_no_std.stderr b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.stderr
new file mode 100644
index 000000000..2b3b6873c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_spin_loop_no_std.stderr
@@ -0,0 +1,10 @@
+error: busy-waiting loop should at least have a spin loop hint
+ --> $DIR/missing_spin_loop_no_std.rs:13:37
+ |
+LL | while b.load(Ordering::Acquire) {}
+ | ^^ help: try this: `{ core::hint::spin_loop() }`
+ |
+ = note: `-D clippy::missing-spin-loop` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
new file mode 100644
index 000000000..a7b36d53c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
@@ -0,0 +1,43 @@
+// run-rustfix
+
+#![allow(
+ dead_code,
+ unused_variables,
+ overflowing_literals,
+ clippy::excessive_precision,
+ clippy::inconsistent_digit_grouping,
+ clippy::unusual_byte_groupings
+)]
+
+fn main() {
+ let fail14 = 2_i32;
+ let fail15 = 4_i64;
+ let fail16 = 7_i8; //
+ let fail17 = 23_i16; //
+ let ok18 = 23_128;
+
+ let fail20 = 2_i8; //
+ let fail21 = 4_i16; //
+
+ let ok24 = 12.34_64;
+ let fail25 = 1E2_f32;
+ let fail26 = 43E7_f64;
+ let fail27 = 243E17_f32;
+ let fail28 = 241_251_235E723_f64;
+ let ok29 = 42279.911_32;
+
+ // testing that the suggestion actually fits in its type
+ let fail30 = 127_i8; // should be i8
+ let fail31 = 240_u8; // should be u8
+ let ok32 = 360_8; // doesnt fit in either, should be ignored
+ let fail33 = 0x1234_i16;
+ let fail34 = 0xABCD_u16;
+ let ok35 = 0x12345_16;
+ let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64
+
+ // issue #6129
+ let ok37 = 123_32.123;
+ let ok38 = 124_64.0;
+
+ let _ = 1.123_45E1_f32;
+}
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
new file mode 100644
index 000000000..c97b31965
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
@@ -0,0 +1,43 @@
+// run-rustfix
+
+#![allow(
+ dead_code,
+ unused_variables,
+ overflowing_literals,
+ clippy::excessive_precision,
+ clippy::inconsistent_digit_grouping,
+ clippy::unusual_byte_groupings
+)]
+
+fn main() {
+ let fail14 = 2_32;
+ let fail15 = 4_64;
+ let fail16 = 7_8; //
+ let fail17 = 23_16; //
+ let ok18 = 23_128;
+
+ let fail20 = 2__8; //
+ let fail21 = 4___16; //
+
+ let ok24 = 12.34_64;
+ let fail25 = 1E2_32;
+ let fail26 = 43E7_64;
+ let fail27 = 243E17_32;
+ let fail28 = 241251235E723_64;
+ let ok29 = 42279.911_32;
+
+ // testing that the suggestion actually fits in its type
+ let fail30 = 127_8; // should be i8
+ let fail31 = 240_8; // should be u8
+ let ok32 = 360_8; // doesnt fit in either, should be ignored
+ let fail33 = 0x1234_16;
+ let fail34 = 0xABCD_16;
+ let ok35 = 0x12345_16;
+ let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+
+ // issue #6129
+ let ok37 = 123_32.123;
+ let ok38 = 124_64.0;
+
+ let _ = 1.12345E1_32;
+}
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr
new file mode 100644
index 000000000..fb761d9bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr
@@ -0,0 +1,100 @@
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:13:18
+ |
+LL | let fail14 = 2_32;
+ | ^^^^ help: did you mean to write: `2_i32`
+ |
+ = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:14:18
+ |
+LL | let fail15 = 4_64;
+ | ^^^^ help: did you mean to write: `4_i64`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:15:18
+ |
+LL | let fail16 = 7_8; //
+ | ^^^ help: did you mean to write: `7_i8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:16:18
+ |
+LL | let fail17 = 23_16; //
+ | ^^^^^ help: did you mean to write: `23_i16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:19:18
+ |
+LL | let fail20 = 2__8; //
+ | ^^^^ help: did you mean to write: `2_i8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:20:18
+ |
+LL | let fail21 = 4___16; //
+ | ^^^^^^ help: did you mean to write: `4_i16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:23:18
+ |
+LL | let fail25 = 1E2_32;
+ | ^^^^^^ help: did you mean to write: `1E2_f32`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:24:18
+ |
+LL | let fail26 = 43E7_64;
+ | ^^^^^^^ help: did you mean to write: `43E7_f64`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:25:18
+ |
+LL | let fail27 = 243E17_32;
+ | ^^^^^^^^^ help: did you mean to write: `243E17_f32`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:26:18
+ |
+LL | let fail28 = 241251235E723_64;
+ | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:30:18
+ |
+LL | let fail30 = 127_8; // should be i8
+ | ^^^^^ help: did you mean to write: `127_i8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:31:18
+ |
+LL | let fail31 = 240_8; // should be u8
+ | ^^^^^ help: did you mean to write: `240_u8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:33:18
+ |
+LL | let fail33 = 0x1234_16;
+ | ^^^^^^^^^ help: did you mean to write: `0x1234_i16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:34:18
+ |
+LL | let fail34 = 0xABCD_16;
+ | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:36:18
+ |
+LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:42:13
+ |
+LL | let _ = 1.12345E1_32;
+ | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mixed_read_write_in_expression.rs b/src/tools/clippy/tests/ui/mixed_read_write_in_expression.rs
new file mode 100644
index 000000000..7640057ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mixed_read_write_in_expression.rs
@@ -0,0 +1,112 @@
+#[warn(clippy::mixed_read_write_in_expression)]
+#[allow(
+ unused_assignments,
+ unused_variables,
+ clippy::no_effect,
+ dead_code,
+ clippy::blacklisted_name
+)]
+fn main() {
+ let mut x = 0;
+ let a = {
+ x = 1;
+ 1
+ } + x;
+
+ // Example from iss#277
+ x += {
+ x = 20;
+ 2
+ };
+
+ // Does it work in weird places?
+ // ...in the base for a struct expression?
+ struct Foo {
+ a: i32,
+ b: i32,
+ };
+ let base = Foo { a: 4, b: 5 };
+ let foo = Foo {
+ a: x,
+ ..{
+ x = 6;
+ base
+ }
+ };
+ // ...inside a closure?
+ let closure = || {
+ let mut x = 0;
+ x += {
+ x = 20;
+ 2
+ };
+ };
+ // ...not across a closure?
+ let mut y = 0;
+ let b = (y, || y = 1);
+
+ // && and || evaluate left-to-right.
+ let a = {
+ x = 1;
+ true
+ } && (x == 3);
+ let a = {
+ x = 1;
+ true
+ } || (x == 3);
+
+ // Make sure we don't get confused by alpha conversion.
+ let a = {
+ let mut x = 1;
+ x = 2;
+ 1
+ } + x;
+
+ // No warning if we don't read the variable...
+ x = {
+ x = 20;
+ 2
+ };
+ // ...if the assignment is in a closure...
+ let b = {
+ || {
+ x = 1;
+ };
+ 1
+ } + x;
+ // ... or the access is under an address.
+ let b = (
+ {
+ let p = &x;
+ 1
+ },
+ {
+ x = 1;
+ x
+ },
+ );
+
+ // Limitation: l-values other than simple variables don't trigger
+ // the warning.
+ let mut tup = (0, 0);
+ let c = {
+ tup.0 = 1;
+ 1
+ } + tup.0;
+ // Limitation: you can get away with a read under address-of.
+ let mut z = 0;
+ let b = (
+ &{
+ z = x;
+ x
+ },
+ {
+ x = 3;
+ x
+ },
+ );
+}
+
+async fn issue_6925() {
+ let _ = vec![async { true }.await, async { false }.await];
+}
diff --git a/src/tools/clippy/tests/ui/mixed_read_write_in_expression.stderr b/src/tools/clippy/tests/ui/mixed_read_write_in_expression.stderr
new file mode 100644
index 000000000..2e951cdbc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mixed_read_write_in_expression.stderr
@@ -0,0 +1,51 @@
+error: unsequenced read of `x`
+ --> $DIR/mixed_read_write_in_expression.rs:14:9
+ |
+LL | } + x;
+ | ^
+ |
+ = note: `-D clippy::mixed-read-write-in-expression` implied by `-D warnings`
+note: whether read occurs before this write depends on evaluation order
+ --> $DIR/mixed_read_write_in_expression.rs:12:9
+ |
+LL | x = 1;
+ | ^^^^^
+
+error: unsequenced read of `x`
+ --> $DIR/mixed_read_write_in_expression.rs:17:5
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
+ --> $DIR/mixed_read_write_in_expression.rs:18:9
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: unsequenced read of `x`
+ --> $DIR/mixed_read_write_in_expression.rs:30:12
+ |
+LL | a: x,
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
+ --> $DIR/mixed_read_write_in_expression.rs:32:13
+ |
+LL | x = 6;
+ | ^^^^^
+
+error: unsequenced read of `x`
+ --> $DIR/mixed_read_write_in_expression.rs:39:9
+ |
+LL | x += {
+ | ^
+ |
+note: whether read occurs before this write depends on evaluation order
+ --> $DIR/mixed_read_write_in_expression.rs:40:13
+ |
+LL | x = 20;
+ | ^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/module_inception.rs b/src/tools/clippy/tests/ui/module_inception.rs
new file mode 100644
index 000000000..a23aba916
--- /dev/null
+++ b/src/tools/clippy/tests/ui/module_inception.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::module_inception)]
+
+mod foo {
+ mod bar {
+ mod bar {
+ mod foo {}
+ }
+ mod foo {}
+ }
+ mod foo {
+ mod bar {}
+ }
+}
+
+// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>.
+mod bar {
+ #[allow(clippy::module_inception)]
+ mod bar {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/module_inception.stderr b/src/tools/clippy/tests/ui/module_inception.stderr
new file mode 100644
index 000000000..77564dce9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/module_inception.stderr
@@ -0,0 +1,20 @@
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:5:9
+ |
+LL | / mod bar {
+LL | | mod foo {}
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::module-inception` implied by `-D warnings`
+
+error: module has the same name as its containing module
+ --> $DIR/module_inception.rs:10:5
+ |
+LL | / mod foo {
+LL | | mod bar {}
+LL | | }
+ | |_____^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs
new file mode 100644
index 000000000..ebaa77cc2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs
@@ -0,0 +1,18 @@
+// compile-flags: --test
+
+#![warn(clippy::module_name_repetitions)]
+#![allow(dead_code)]
+
+mod foo {
+ pub fn foo() {}
+ pub fn foo_bar() {}
+ pub fn bar_foo() {}
+ pub struct FooCake;
+ pub enum CakeFoo {}
+ pub struct Foo7Bar;
+
+ // Should not warn
+ pub struct Foobar;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr
new file mode 100644
index 000000000..3f343a3e4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr
@@ -0,0 +1,34 @@
+error: item name starts with its containing module's name
+ --> $DIR/module_name_repetitions.rs:8:5
+ |
+LL | pub fn foo_bar() {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::module-name-repetitions` implied by `-D warnings`
+
+error: item name ends with its containing module's name
+ --> $DIR/module_name_repetitions.rs:9:5
+ |
+LL | pub fn bar_foo() {}
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: item name starts with its containing module's name
+ --> $DIR/module_name_repetitions.rs:10:5
+ |
+LL | pub struct FooCake;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: item name ends with its containing module's name
+ --> $DIR/module_name_repetitions.rs:11:5
+ |
+LL | pub enum CakeFoo {}
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: item name starts with its containing module's name
+ --> $DIR/module_name_repetitions.rs:12:5
+ |
+LL | pub struct Foo7Bar;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs
new file mode 100644
index 000000000..b1861f07c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs
@@ -0,0 +1,29 @@
+#![warn(clippy::modulo_arithmetic)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+
+fn main() {
+ // Lint when both sides are const and of the opposite sign
+ -1.6 % 2.1;
+ 1.6 % -2.1;
+ (1.1 - 2.3) % (1.1 + 2.3);
+ (1.1 + 2.3) % (1.1 - 2.3);
+
+ // Lint on floating point numbers
+ let a_f32: f32 = -1.6;
+ let mut b_f32: f32 = 2.1;
+ a_f32 % b_f32;
+ b_f32 % a_f32;
+ b_f32 %= a_f32;
+
+ let a_f64: f64 = -1.6;
+ let mut b_f64: f64 = 2.1;
+ a_f64 % b_f64;
+ b_f64 % a_f64;
+ b_f64 %= a_f64;
+
+ // No lint when both sides are const and of the same sign
+ 1.6 % 2.1;
+ -1.6 % -2.1;
+ (1.1 + 2.3) % (-1.1 + 2.3);
+ (-1.1 - 2.3) % (1.1 - 2.3);
+}
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr
new file mode 100644
index 000000000..97844aaaa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr
@@ -0,0 +1,83 @@
+error: you are using modulo operator on constants with different signs: `-1.600 % 2.100`
+ --> $DIR/modulo_arithmetic_float.rs:6:5
+ |
+LL | -1.6 % 2.1;
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `1.600 % -2.100`
+ --> $DIR/modulo_arithmetic_float.rs:7:5
+ |
+LL | 1.6 % -2.1;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `-1.200 % 3.400`
+ --> $DIR/modulo_arithmetic_float.rs:8:5
+ |
+LL | (1.1 - 2.3) % (1.1 + 2.3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on constants with different signs: `3.400 % -1.200`
+ --> $DIR/modulo_arithmetic_float.rs:9:5
+ |
+LL | (1.1 + 2.3) % (1.1 - 2.3);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:14:5
+ |
+LL | a_f32 % b_f32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:15:5
+ |
+LL | b_f32 % a_f32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:16:5
+ |
+LL | b_f32 %= a_f32;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:20:5
+ |
+LL | a_f64 % b_f64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:21:5
+ |
+LL | b_f64 % a_f64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_float.rs:22:5
+ |
+LL | b_f64 %= a_f64;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
new file mode 100644
index 000000000..fc1acc39e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
@@ -0,0 +1,83 @@
+#![warn(clippy::modulo_arithmetic)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+
+fn main() {
+ // Lint on signed integral numbers
+ let a = -1;
+ let mut b = 2;
+ a % b;
+ b % a;
+ b %= a;
+
+ let a_i8: i8 = 1;
+ let mut b_i8: i8 = 2;
+ a_i8 % b_i8;
+ b_i8 %= a_i8;
+
+ let a_i16: i16 = 1;
+ let mut b_i16: i16 = 2;
+ a_i16 % b_i16;
+ b_i16 %= a_i16;
+
+ let a_i32: i32 = 1;
+ let mut b_i32: i32 = 2;
+ a_i32 % b_i32;
+ b_i32 %= a_i32;
+
+ let a_i64: i64 = 1;
+ let mut b_i64: i64 = 2;
+ a_i64 % b_i64;
+ b_i64 %= a_i64;
+
+ let a_i128: i128 = 1;
+ let mut b_i128: i128 = 2;
+ a_i128 % b_i128;
+ b_i128 %= a_i128;
+
+ let a_isize: isize = 1;
+ let mut b_isize: isize = 2;
+ a_isize % b_isize;
+ b_isize %= a_isize;
+
+ let a = 1;
+ let mut b = 2;
+ a % b;
+ b %= a;
+
+ // No lint on unsigned integral value
+ let a_u8: u8 = 17;
+ let b_u8: u8 = 3;
+ a_u8 % b_u8;
+ let mut a_u8: u8 = 1;
+ a_u8 %= 2;
+
+ let a_u16: u16 = 17;
+ let b_u16: u16 = 3;
+ a_u16 % b_u16;
+ let mut a_u16: u16 = 1;
+ a_u16 %= 2;
+
+ let a_u32: u32 = 17;
+ let b_u32: u32 = 3;
+ a_u32 % b_u32;
+ let mut a_u32: u32 = 1;
+ a_u32 %= 2;
+
+ let a_u64: u64 = 17;
+ let b_u64: u64 = 3;
+ a_u64 % b_u64;
+ let mut a_u64: u64 = 1;
+ a_u64 %= 2;
+
+ let a_u128: u128 = 17;
+ let b_u128: u128 = 3;
+ a_u128 % b_u128;
+ let mut a_u128: u128 = 1;
+ a_u128 %= 2;
+
+ let a_usize: usize = 17;
+ let b_usize: usize = 3;
+ a_usize % b_usize;
+ let mut a_usize: usize = 1;
+ a_usize %= 2;
+}
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr
new file mode 100644
index 000000000..f71adf5b0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr
@@ -0,0 +1,156 @@
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:8:5
+ |
+LL | a % b;
+ | ^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:9:5
+ |
+LL | b % a;
+ | ^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:10:5
+ |
+LL | b %= a;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:14:5
+ |
+LL | a_i8 % b_i8;
+ | ^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:15:5
+ |
+LL | b_i8 %= a_i8;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:19:5
+ |
+LL | a_i16 % b_i16;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:20:5
+ |
+LL | b_i16 %= a_i16;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:24:5
+ |
+LL | a_i32 % b_i32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:25:5
+ |
+LL | b_i32 %= a_i32;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:29:5
+ |
+LL | a_i64 % b_i64;
+ | ^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:30:5
+ |
+LL | b_i64 %= a_i64;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:34:5
+ |
+LL | a_i128 % b_i128;
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:35:5
+ |
+LL | b_i128 %= a_i128;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:39:5
+ |
+LL | a_isize % b_isize;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:40:5
+ |
+LL | b_isize %= a_isize;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:44:5
+ |
+LL | a % b;
+ | ^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+ --> $DIR/modulo_arithmetic_integral.rs:45:5
+ |
+LL | b %= a;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs
new file mode 100644
index 000000000..3ebe46bc5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs
@@ -0,0 +1,42 @@
+#![warn(clippy::modulo_arithmetic)]
+#![allow(
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::modulo_one,
+ clippy::identity_op
+)]
+
+fn main() {
+ // Lint when both sides are const and of the opposite sign
+ -1 % 2;
+ 1 % -2;
+ (1 - 2) % (1 + 2);
+ (1 + 2) % (1 - 2);
+ 35 * (7 - 4 * 2) % (-500 * -600);
+
+ -1i8 % 2i8;
+ 1i8 % -2i8;
+ -1i16 % 2i16;
+ 1i16 % -2i16;
+ -1i32 % 2i32;
+ 1i32 % -2i32;
+ -1i64 % 2i64;
+ 1i64 % -2i64;
+ -1i128 % 2i128;
+ 1i128 % -2i128;
+ -1isize % 2isize;
+ 1isize % -2isize;
+
+ // No lint when both sides are const and of the same sign
+ 1 % 2;
+ -1 % -2;
+ (1 + 2) % (-1 + 2);
+ (-1 - 2) % (1 - 2);
+
+ 1u8 % 2u8;
+ 1u16 % 2u16;
+ 1u32 % 2u32;
+ 1u64 % 2u64;
+ 1u128 % 2u128;
+ 1usize % 2usize;
+}
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr
new file mode 100644
index 000000000..11b5f7746
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr
@@ -0,0 +1,156 @@
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:11:5
+ |
+LL | -1 % 2;
+ | ^^^^^^
+ |
+ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:12:5
+ |
+LL | 1 % -2;
+ | ^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 3`
+ --> $DIR/modulo_arithmetic_integral_const.rs:13:5
+ |
+LL | (1 - 2) % (1 + 2);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `3 % -1`
+ --> $DIR/modulo_arithmetic_integral_const.rs:14:5
+ |
+LL | (1 + 2) % (1 - 2);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-35 % 300000`
+ --> $DIR/modulo_arithmetic_integral_const.rs:15:5
+ |
+LL | 35 * (7 - 4 * 2) % (-500 * -600);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:17:5
+ |
+LL | -1i8 % 2i8;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:18:5
+ |
+LL | 1i8 % -2i8;
+ | ^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:19:5
+ |
+LL | -1i16 % 2i16;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:20:5
+ |
+LL | 1i16 % -2i16;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:21:5
+ |
+LL | -1i32 % 2i32;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:22:5
+ |
+LL | 1i32 % -2i32;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:23:5
+ |
+LL | -1i64 % 2i64;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:24:5
+ |
+LL | 1i64 % -2i64;
+ | ^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:25:5
+ |
+LL | -1i128 % 2i128;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:26:5
+ |
+LL | 1i128 % -2i128;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `-1 % 2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:27:5
+ |
+LL | -1isize % 2isize;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on constants with different signs: `1 % -2`
+ --> $DIR/modulo_arithmetic_integral_const.rs:28:5
+ |
+LL | 1isize % -2isize;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: double check for expected result especially when interoperating with different languages
+ = note: or consider using `rem_euclid` or similar function
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs
new file mode 100644
index 000000000..adff08e5d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_one.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::modulo_one)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)]
+
+static STATIC_ONE: usize = 2 - 1;
+static STATIC_NEG_ONE: i64 = 1 - 2;
+
+fn main() {
+ 10 % 1;
+ 10 % -1;
+ 10 % 2;
+ i32::MIN % (-1); // also caught by rustc
+
+ const ONE: u32 = 1 * 1;
+ const NEG_ONE: i64 = 1 - 2;
+ const INT_MIN: i64 = i64::MIN;
+
+ 2 % ONE;
+ 5 % STATIC_ONE; // NOT caught by lint
+ 2 % NEG_ONE;
+ 5 % STATIC_NEG_ONE; // NOT caught by lint
+ INT_MIN % NEG_ONE; // also caught by rustc
+ INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
+}
diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr
new file mode 100644
index 000000000..04ecdef5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/modulo_one.stderr
@@ -0,0 +1,60 @@
+error: this operation will panic at runtime
+ --> $DIR/modulo_one.rs:11:5
+ |
+LL | i32::MIN % (-1); // also caught by rustc
+ | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow
+ |
+ = note: `#[deny(unconditional_panic)]` on by default
+
+error: this operation will panic at runtime
+ --> $DIR/modulo_one.rs:21:5
+ |
+LL | INT_MIN % NEG_ONE; // also caught by rustc
+ | ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
+
+error: this operation will panic at runtime
+ --> $DIR/modulo_one.rs:22:5
+ |
+LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
+
+error: any number modulo 1 will be 0
+ --> $DIR/modulo_one.rs:8:5
+ |
+LL | 10 % 1;
+ | ^^^^^^
+ |
+ = note: `-D clippy::modulo-one` implied by `-D warnings`
+
+error: any number modulo -1 will panic/overflow or result in 0
+ --> $DIR/modulo_one.rs:9:5
+ |
+LL | 10 % -1;
+ | ^^^^^^^
+
+error: any number modulo -1 will panic/overflow or result in 0
+ --> $DIR/modulo_one.rs:11:5
+ |
+LL | i32::MIN % (-1); // also caught by rustc
+ | ^^^^^^^^^^^^^^^
+
+error: any number modulo 1 will be 0
+ --> $DIR/modulo_one.rs:17:5
+ |
+LL | 2 % ONE;
+ | ^^^^^^^
+
+error: any number modulo -1 will panic/overflow or result in 0
+ --> $DIR/modulo_one.rs:19:5
+ |
+LL | 2 % NEG_ONE;
+ | ^^^^^^^^^^^
+
+error: any number modulo -1 will panic/overflow or result in 0
+ --> $DIR/modulo_one.rs:21:5
+ |
+LL | INT_MIN % NEG_ONE; // also caught by rustc
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed
new file mode 100644
index 000000000..04a74a009
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed
@@ -0,0 +1,93 @@
+// run-rustfix
+#![feature(never_type)]
+#![allow(unused_mut, unused_tuple_struct_fields, clippy::redundant_allocation)]
+#![warn(clippy::must_use_candidate)]
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+
+pub struct MyAtomic(AtomicBool);
+pub struct MyPure;
+
+#[must_use] pub fn pure(i: u8) -> u8 {
+ i
+}
+
+impl MyPure {
+ #[must_use] pub fn inherent_pure(&self) -> u8 {
+ 0
+ }
+}
+
+pub trait MyPureTrait {
+ fn trait_pure(&self, i: u32) -> u32 {
+ self.trait_impl_pure(i) + 1
+ }
+
+ fn trait_impl_pure(&self, i: u32) -> u32;
+}
+
+impl MyPureTrait for MyPure {
+ fn trait_impl_pure(&self, i: u32) -> u32 {
+ i
+ }
+}
+
+pub fn without_result() {
+ // OK
+}
+
+pub fn impure_primitive(i: &mut u8) -> u8 {
+ *i
+}
+
+pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
+ f(0)
+}
+
+#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ true
+}
+
+pub fn quoth_the_raven(_more: !) -> u32 {
+ unimplemented!();
+}
+
+pub fn atomics(b: &AtomicBool) -> bool {
+ b.load(Ordering::SeqCst)
+}
+
+#[must_use] pub fn rcd(_x: Rc<u32>) -> bool {
+ true
+}
+
+pub fn rcmut(_x: Rc<&mut u32>) -> bool {
+ true
+}
+
+#[must_use] pub fn arcd(_x: Arc<u32>) -> bool {
+ false
+}
+
+pub fn inner_types(_m: &MyAtomic) -> bool {
+ true
+}
+
+static mut COUNTER: usize = 0;
+
+/// # Safety
+///
+/// Don't ever call this from multiple threads
+pub unsafe fn mutates_static() -> usize {
+ COUNTER += 1;
+ COUNTER
+}
+
+#[no_mangle]
+pub fn unmangled(i: bool) -> bool {
+ !i
+}
+
+fn main() {
+ assert_eq!(1, pure(1));
+}
diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs
new file mode 100644
index 000000000..f04122f4e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_candidates.rs
@@ -0,0 +1,93 @@
+// run-rustfix
+#![feature(never_type)]
+#![allow(unused_mut, unused_tuple_struct_fields, clippy::redundant_allocation)]
+#![warn(clippy::must_use_candidate)]
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+
+pub struct MyAtomic(AtomicBool);
+pub struct MyPure;
+
+pub fn pure(i: u8) -> u8 {
+ i
+}
+
+impl MyPure {
+ pub fn inherent_pure(&self) -> u8 {
+ 0
+ }
+}
+
+pub trait MyPureTrait {
+ fn trait_pure(&self, i: u32) -> u32 {
+ self.trait_impl_pure(i) + 1
+ }
+
+ fn trait_impl_pure(&self, i: u32) -> u32;
+}
+
+impl MyPureTrait for MyPure {
+ fn trait_impl_pure(&self, i: u32) -> u32 {
+ i
+ }
+}
+
+pub fn without_result() {
+ // OK
+}
+
+pub fn impure_primitive(i: &mut u8) -> u8 {
+ *i
+}
+
+pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
+ f(0)
+}
+
+pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ true
+}
+
+pub fn quoth_the_raven(_more: !) -> u32 {
+ unimplemented!();
+}
+
+pub fn atomics(b: &AtomicBool) -> bool {
+ b.load(Ordering::SeqCst)
+}
+
+pub fn rcd(_x: Rc<u32>) -> bool {
+ true
+}
+
+pub fn rcmut(_x: Rc<&mut u32>) -> bool {
+ true
+}
+
+pub fn arcd(_x: Arc<u32>) -> bool {
+ false
+}
+
+pub fn inner_types(_m: &MyAtomic) -> bool {
+ true
+}
+
+static mut COUNTER: usize = 0;
+
+/// # Safety
+///
+/// Don't ever call this from multiple threads
+pub unsafe fn mutates_static() -> usize {
+ COUNTER += 1;
+ COUNTER
+}
+
+#[no_mangle]
+pub fn unmangled(i: bool) -> bool {
+ !i
+}
+
+fn main() {
+ assert_eq!(1, pure(1));
+}
diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr
new file mode 100644
index 000000000..0fa3849d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr
@@ -0,0 +1,34 @@
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:12:1
+ |
+LL | pub fn pure(i: u8) -> u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8`
+ |
+ = note: `-D clippy::must-use-candidate` implied by `-D warnings`
+
+error: this method could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:17:5
+ |
+LL | pub fn inherent_pure(&self) -> u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:48:1
+ |
+LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:60:1
+ |
+LL | pub fn rcd(_x: Rc<u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:68:1
+ |
+LL | pub fn arcd(_x: Arc<u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed
new file mode 100644
index 000000000..6c9aa434a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_unit.fixed
@@ -0,0 +1,26 @@
+//run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::must_use_unit)]
+#![allow(clippy::unused_unit)]
+
+#[macro_use]
+extern crate macro_rules;
+
+
+pub fn must_use_default() {}
+
+
+pub fn must_use_unit() -> () {}
+
+
+pub fn must_use_with_note() {}
+
+fn main() {
+ must_use_default();
+ must_use_unit();
+ must_use_with_note();
+
+ // We should not lint in external macros
+ must_use_unit!();
+}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs
new file mode 100644
index 000000000..8a395dc28
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_unit.rs
@@ -0,0 +1,26 @@
+//run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::must_use_unit)]
+#![allow(clippy::unused_unit)]
+
+#[macro_use]
+extern crate macro_rules;
+
+#[must_use]
+pub fn must_use_default() {}
+
+#[must_use]
+pub fn must_use_unit() -> () {}
+
+#[must_use = "With note"]
+pub fn must_use_with_note() {}
+
+fn main() {
+ must_use_default();
+ must_use_unit();
+ must_use_with_note();
+
+ // We should not lint in external macros
+ must_use_unit!();
+}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr
new file mode 100644
index 000000000..15e0906b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/must_use_unit.stderr
@@ -0,0 +1,28 @@
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:11:1
+ |
+LL | #[must_use]
+ | ----------- help: remove the attribute
+LL | pub fn must_use_default() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::must-use-unit` implied by `-D warnings`
+
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:14:1
+ |
+LL | #[must_use]
+ | ----------- help: remove the attribute
+LL | pub fn must_use_unit() -> () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:17:1
+ |
+LL | #[must_use = "With note"]
+ | ------------------------- help: remove the attribute
+LL | pub fn must_use_with_note() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs
new file mode 100644
index 000000000..370dbd588
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_from_ref.rs
@@ -0,0 +1,54 @@
+#![allow(unused)]
+#![warn(clippy::mut_from_ref)]
+
+struct Foo;
+
+impl Foo {
+ fn this_wont_hurt_a_bit(&self) -> &mut Foo {
+ unsafe { unimplemented!() }
+ }
+}
+
+trait Ouch {
+ fn ouch(x: &Foo) -> &mut Foo;
+}
+
+impl Ouch for Foo {
+ fn ouch(x: &Foo) -> &mut Foo {
+ unsafe { unimplemented!() }
+ }
+}
+
+fn fail(x: &u32) -> &mut u16 {
+ unsafe { unimplemented!() }
+}
+
+fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
+ unsafe { unimplemented!() }
+}
+
+fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
+ unsafe { unimplemented!() }
+}
+
+// this is OK, because the result borrows y
+fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
+ unsafe { unimplemented!() }
+}
+
+// this is also OK, because the result could borrow y
+fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
+ unsafe { unimplemented!() }
+}
+
+unsafe fn also_broken(x: &u32) -> &mut u32 {
+ unimplemented!()
+}
+
+fn without_unsafe(x: &u32) -> &mut u32 {
+ unimplemented!()
+}
+
+fn main() {
+ //TODO
+}
diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr
new file mode 100644
index 000000000..b76d6a13f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr
@@ -0,0 +1,75 @@
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:7:39
+ |
+LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo {
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::mut-from-ref` implied by `-D warnings`
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:7:29
+ |
+LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo {
+ | ^^^^^
+
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:13:25
+ |
+LL | fn ouch(x: &Foo) -> &mut Foo;
+ | ^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:13:16
+ |
+LL | fn ouch(x: &Foo) -> &mut Foo;
+ | ^^^^
+
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:22:21
+ |
+LL | fn fail(x: &u32) -> &mut u16 {
+ | ^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:22:12
+ |
+LL | fn fail(x: &u32) -> &mut u16 {
+ | ^^^^
+
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:26:50
+ |
+LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
+ | ^^^^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:26:25
+ |
+LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
+ | ^^^^^^^
+
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:30:67
+ |
+LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
+ | ^^^^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:30:27
+ |
+LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
+ | ^^^^^^^ ^^^^^^^
+
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:44:35
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:44:26
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs
new file mode 100644
index 000000000..1c0ba6645
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_key.rs
@@ -0,0 +1,85 @@
+use std::cell::Cell;
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::hash::{Hash, Hasher};
+use std::rc::Rc;
+use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use std::sync::Arc;
+
+struct Key(AtomicUsize);
+
+impl Clone for Key {
+ fn clone(&self) -> Self {
+ Key(AtomicUsize::new(self.0.load(Relaxed)))
+ }
+}
+
+impl PartialEq for Key {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.load(Relaxed) == other.0.load(Relaxed)
+ }
+}
+
+impl Eq for Key {}
+
+impl Hash for Key {
+ fn hash<H: Hasher>(&self, h: &mut H) {
+ self.0.load(Relaxed).hash(h);
+ }
+}
+
+fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ let _other: HashMap<Key, bool> = HashMap::new();
+ m.keys().cloned().collect()
+}
+
+fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
+
+// Raw pointers are hashed by the address they point to, so it doesn't matter if they point to a
+// type with interior mutability. See:
+// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
+// - std lib: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
+// So these are OK:
+fn raw_ptr_is_ok(_m: &mut HashMap<*const Key, ()>) {}
+fn raw_mut_ptr_is_ok(_m: &mut HashMap<*mut Key, ()>) {}
+
+#[allow(unused)]
+trait Trait {
+ type AssociatedType;
+
+ fn trait_fn(&self, set: HashSet<Self::AssociatedType>);
+}
+
+fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
+ // nothing to see here, move along
+}
+
+fn tuples<U>(_m: &mut HashMap<((), U), ()>) {}
+
+fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
+
+fn main() {
+ let _ = should_not_take_this_arg(&mut HashMap::new(), 1);
+ this_is_ok(&mut HashMap::new());
+ tuples::<Key>(&mut HashMap::new());
+ tuples::<()>(&mut HashMap::new());
+ tuples_bad::<()>(&mut HashMap::new());
+
+ raw_ptr_is_ok(&mut HashMap::new());
+ raw_mut_ptr_is_ok(&mut HashMap::new());
+
+ let _map = HashMap::<Cell<usize>, usize>::new();
+ let _map = HashMap::<&mut Cell<usize>, usize>::new();
+ let _map = HashMap::<&mut usize, usize>::new();
+ // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+ let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+ let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+ let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+ let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+ let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+ // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+ let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
+}
diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr
new file mode 100644
index 000000000..25dd029b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_key.stderr
@@ -0,0 +1,106 @@
+error: mutable key type
+ --> $DIR/mut_key.rs:30:32
+ |
+LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mutable-key-type` implied by `-D warnings`
+
+error: mutable key type
+ --> $DIR/mut_key.rs:30:72
+ |
+LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
+ | ^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:31:5
+ |
+LL | let _other: HashMap<Key, bool> = HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:58:22
+ |
+LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:70:5
+ |
+LL | let _map = HashMap::<Cell<usize>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:71:5
+ |
+LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:72:5
+ |
+LL | let _map = HashMap::<&mut usize, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:74:5
+ |
+LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:75:5
+ |
+LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:76:5
+ |
+LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:77:5
+ |
+LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:78:5
+ |
+LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:79:5
+ |
+LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:80:5
+ |
+LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:82:5
+ |
+LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:83:5
+ |
+LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:84:5
+ |
+LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs
new file mode 100644
index 000000000..be854d941
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mut.rs
@@ -0,0 +1,59 @@
+// aux-build:macro_rules.rs
+
+#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
+#![warn(clippy::mut_mut)]
+
+#[macro_use]
+extern crate macro_rules;
+
+fn fun(x: &mut &mut u32) -> bool {
+ **x > 0
+}
+
+fn less_fun(x: *mut *mut u32) {
+ let y = x;
+}
+
+macro_rules! mut_ptr {
+ ($p:expr) => {
+ &mut $p
+ };
+}
+
+#[allow(unused_mut, unused_variables)]
+fn main() {
+ let mut x = &mut &mut 1u32;
+ {
+ let mut y = &mut x;
+ }
+
+ if fun(x) {
+ let y: &mut &mut u32 = &mut &mut 2;
+ **y + **x;
+ }
+
+ if fun(x) {
+ let y: &mut &mut &mut u32 = &mut &mut &mut 2;
+ ***y + **x;
+ }
+
+ let mut z = mut_ptr!(&mut 3u32);
+}
+
+fn issue939() {
+ let array = [5, 6, 7, 8, 9];
+ let mut args = array.iter().skip(2);
+ for &arg in &mut args {
+ println!("{}", arg);
+ }
+
+ let args = &mut args;
+ for arg in args {
+ println!(":{}", arg);
+ }
+}
+
+fn issue6922() {
+ // do not lint from an external macro
+ mut_mut!();
+}
diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr
new file mode 100644
index 000000000..6820a85aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mut.stderr
@@ -0,0 +1,63 @@
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:9:11
+ |
+LL | fn fun(x: &mut &mut u32) -> bool {
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mut-mut` implied by `-D warnings`
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:25:17
+ |
+LL | let mut x = &mut &mut 1u32;
+ | ^^^^^^^^^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:19:9
+ |
+LL | &mut $p
+ | ^^^^^^^
+...
+LL | let mut z = mut_ptr!(&mut 3u32);
+ | ------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this expression mutably borrows a mutable reference. Consider reborrowing
+ --> $DIR/mut_mut.rs:27:21
+ |
+LL | let mut y = &mut x;
+ | ^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:31:32
+ |
+LL | let y: &mut &mut u32 = &mut &mut 2;
+ | ^^^^^^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:31:16
+ |
+LL | let y: &mut &mut u32 = &mut &mut 2;
+ | ^^^^^^^^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:36:37
+ |
+LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
+ | ^^^^^^^^^^^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:36:16
+ |
+LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: generally you want to avoid `&mut &mut _` if possible
+ --> $DIR/mut_mut.rs:36:21
+ |
+LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed
new file mode 100644
index 000000000..36bc52e33
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![allow(dead_code, unused_mut)]
+#![warn(clippy::mut_mutex_lock)]
+
+use std::sync::{Arc, Mutex};
+
+fn mut_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+
+ let mut value = value_mutex.get_mut().unwrap();
+ *value += 1;
+}
+
+fn no_owned_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let mut value = value_rc.lock().unwrap();
+ *value += 1;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.rs b/src/tools/clippy/tests/ui/mut_mutex_lock.rs
new file mode 100644
index 000000000..ea60df5ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![allow(dead_code, unused_mut)]
+#![warn(clippy::mut_mutex_lock)]
+
+use std::sync::{Arc, Mutex};
+
+fn mut_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+
+ let mut value = value_mutex.lock().unwrap();
+ *value += 1;
+}
+
+fn no_owned_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let mut value = value_rc.lock().unwrap();
+ *value += 1;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr
new file mode 100644
index 000000000..21c1b3486
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr
@@ -0,0 +1,10 @@
+error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference
+ --> $DIR/mut_mutex_lock.rs:11:33
+ |
+LL | let mut value = value_mutex.lock().unwrap();
+ | ^^^^ help: change this to: `get_mut`
+ |
+ = note: `-D clippy::mut-mutex-lock` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs
new file mode 100644
index 000000000..e1ae1ef92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_range_bound.rs
@@ -0,0 +1,84 @@
+#![allow(unused)]
+
+fn main() {}
+
+fn mut_range_bound_upper() {
+ let mut m = 4;
+ for i in 0..m {
+ m = 5;
+ } // warning
+}
+
+fn mut_range_bound_lower() {
+ let mut m = 4;
+ for i in m..10 {
+ m *= 2;
+ } // warning
+}
+
+fn mut_range_bound_both() {
+ let mut m = 4;
+ let mut n = 6;
+ for i in m..n {
+ m = 5;
+ n = 7;
+ } // warning (1 for each mutated bound)
+}
+
+fn mut_range_bound_no_mutation() {
+ let mut m = 4;
+ for i in 0..m {
+ continue;
+ } // no warning
+}
+
+fn mut_borrow_range_bound() {
+ let mut m = 4;
+ for i in 0..m {
+ let n = &mut m; // warning
+ *n += 1;
+ }
+}
+
+fn immut_borrow_range_bound() {
+ let mut m = 4;
+ for i in 0..m {
+ let n = &m; // should be no warning?
+ }
+}
+
+fn immut_range_bound() {
+ let m = 4;
+ for i in 0..m {
+ continue;
+ } // no warning
+}
+
+fn mut_range_bound_break() {
+ let mut m = 4;
+ for i in 0..m {
+ if m == 4 {
+ m = 5; // no warning because of immediate break
+ break;
+ }
+ }
+}
+
+fn mut_range_bound_no_immediate_break() {
+ let mut m = 4;
+ for i in 0..m {
+ m = 2; // warning because it is not immediately followed by break
+ if m == 4 {
+ break;
+ }
+ }
+
+ let mut n = 3;
+ for i in n..10 {
+ if n == 4 {
+ n = 1; // FIXME: warning because is is not immediately followed by break
+ let _ = 2;
+ break;
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/mut_range_bound.stderr b/src/tools/clippy/tests/ui/mut_range_bound.stderr
new file mode 100644
index 000000000..4b5a3fc1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_range_bound.stderr
@@ -0,0 +1,59 @@
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:8:9
+ |
+LL | m = 5;
+ | ^
+ |
+ = note: `-D clippy::mut-range-bound` implied by `-D warnings`
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:15:9
+ |
+LL | m *= 2;
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:23:9
+ |
+LL | m = 5;
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:24:9
+ |
+LL | n = 7;
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:38:22
+ |
+LL | let n = &mut m; // warning
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:70:9
+ |
+LL | m = 2; // warning because it is not immediately followed by break
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+ --> $DIR/mut_range_bound.rs:79:13
+ |
+LL | n = 1; // FIXME: warning because is is not immediately followed by break
+ | ^
+ |
+ = note: the range of the loop is unchanged
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs
new file mode 100644
index 000000000..73906121c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_reference.rs
@@ -0,0 +1,43 @@
+#![allow(unused_variables)]
+
+fn takes_an_immutable_reference(a: &i32) {}
+fn takes_a_mutable_reference(a: &mut i32) {}
+
+struct MyStruct;
+
+impl MyStruct {
+ fn takes_an_immutable_reference(&self, a: &i32) {}
+
+ fn takes_a_mutable_reference(&self, a: &mut i32) {}
+}
+
+#[warn(clippy::unnecessary_mut_passed)]
+fn main() {
+ // Functions
+ takes_an_immutable_reference(&mut 42);
+ let as_ptr: fn(&i32) = takes_an_immutable_reference;
+ as_ptr(&mut 42);
+
+ // Methods
+ let my_struct = MyStruct;
+ my_struct.takes_an_immutable_reference(&mut 42);
+
+ // No error
+
+ // Functions
+ takes_an_immutable_reference(&42);
+ let as_ptr: fn(&i32) = takes_an_immutable_reference;
+ as_ptr(&42);
+
+ takes_a_mutable_reference(&mut 42);
+ let as_ptr: fn(&mut i32) = takes_a_mutable_reference;
+ as_ptr(&mut 42);
+
+ let a = &mut 42;
+ takes_an_immutable_reference(a);
+
+ // Methods
+ my_struct.takes_an_immutable_reference(&42);
+ my_struct.takes_a_mutable_reference(&mut 42);
+ my_struct.takes_an_immutable_reference(a);
+}
diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr
new file mode 100644
index 000000000..062d30b26
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_reference.stderr
@@ -0,0 +1,22 @@
+error: the function `takes_an_immutable_reference` doesn't need a mutable reference
+ --> $DIR/mut_reference.rs:17:34
+ |
+LL | takes_an_immutable_reference(&mut 42);
+ | ^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
+
+error: the function `as_ptr` doesn't need a mutable reference
+ --> $DIR/mut_reference.rs:19:12
+ |
+LL | as_ptr(&mut 42);
+ | ^^^^^^^
+
+error: the method `takes_an_immutable_reference` doesn't need a mutable reference
+ --> $DIR/mut_reference.rs:23:44
+ |
+LL | my_struct.takes_an_immutable_reference(&mut 42);
+ | ^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs
new file mode 100644
index 000000000..47b3dad39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mutex_atomic.rs
@@ -0,0 +1,17 @@
+#![warn(clippy::all)]
+#![warn(clippy::mutex_integer)]
+#![warn(clippy::mutex_atomic)]
+#![allow(clippy::borrow_as_ptr)]
+
+fn main() {
+ use std::sync::Mutex;
+ Mutex::new(true);
+ Mutex::new(5usize);
+ Mutex::new(9isize);
+ let mut x = 4u32;
+ Mutex::new(&x as *const u32);
+ Mutex::new(&mut x as *mut u32);
+ Mutex::new(0u32);
+ Mutex::new(0i32);
+ Mutex::new(0f32); // there are no float atomics, so this should not lint
+}
diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr
new file mode 100644
index 000000000..262028a87
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr
@@ -0,0 +1,48 @@
+error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:8:5
+ |
+LL | Mutex::new(true);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mutex-atomic` implied by `-D warnings`
+
+error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:9:5
+ |
+LL | Mutex::new(5usize);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:10:5
+ |
+LL | Mutex::new(9isize);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:12:5
+ |
+LL | Mutex::new(&x as *const u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:13:5
+ |
+LL | Mutex::new(&mut x as *mut u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:14:5
+ |
+LL | Mutex::new(0u32);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::mutex-integer` implied by `-D warnings`
+
+error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
+ --> $DIR/mutex_atomic.rs:15:5
+ |
+LL | Mutex::new(0i32);
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed
new file mode 100644
index 000000000..9da21eb6b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![warn(clippy::needless_arbitrary_self_type)]
+#![allow(unused_mut, clippy::needless_lifetimes)]
+
+pub enum ValType {
+ A,
+ B,
+}
+
+impl ValType {
+ pub fn bad(self) {
+ unimplemented!();
+ }
+
+ pub fn good(self) {
+ unimplemented!();
+ }
+
+ pub fn mut_bad(mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_good(mut self) {
+ unimplemented!();
+ }
+
+ pub fn ref_bad(&self) {
+ unimplemented!();
+ }
+
+ pub fn ref_good(&self) {
+ unimplemented!();
+ }
+
+ pub fn ref_bad_with_lifetime<'a>(&'a self) {
+ unimplemented!();
+ }
+
+ pub fn ref_good_with_lifetime<'a>(&'a self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_bad(&mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_good(&mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_mut_good(mut self: &mut Self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
+ unimplemented!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs
new file mode 100644
index 000000000..17aeaaf97
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![warn(clippy::needless_arbitrary_self_type)]
+#![allow(unused_mut, clippy::needless_lifetimes)]
+
+pub enum ValType {
+ A,
+ B,
+}
+
+impl ValType {
+ pub fn bad(self: Self) {
+ unimplemented!();
+ }
+
+ pub fn good(self) {
+ unimplemented!();
+ }
+
+ pub fn mut_bad(mut self: Self) {
+ unimplemented!();
+ }
+
+ pub fn mut_good(mut self) {
+ unimplemented!();
+ }
+
+ pub fn ref_bad(self: &Self) {
+ unimplemented!();
+ }
+
+ pub fn ref_good(&self) {
+ unimplemented!();
+ }
+
+ pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
+ unimplemented!();
+ }
+
+ pub fn ref_good_with_lifetime<'a>(&'a self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_bad(self: &mut Self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_good(&mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_mut_good(mut self: &mut Self) {
+ unimplemented!();
+ }
+
+ pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
+ unimplemented!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr
new file mode 100644
index 000000000..f4c645d35
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr
@@ -0,0 +1,40 @@
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:12:16
+ |
+LL | pub fn bad(self: Self) {
+ | ^^^^^^^^^^ help: consider to change this parameter to: `self`
+ |
+ = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
+
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:20:20
+ |
+LL | pub fn mut_bad(mut self: Self) {
+ | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:28:20
+ |
+LL | pub fn ref_bad(self: &Self) {
+ | ^^^^^^^^^^^ help: consider to change this parameter to: `&self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:36:38
+ |
+LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
+ | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:44:24
+ |
+LL | pub fn mut_ref_bad(self: &mut Self) {
+ | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type.rs:52:42
+ |
+LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
+ | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs
new file mode 100644
index 000000000..02b43cce2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs
@@ -0,0 +1,46 @@
+// aux-build:proc_macro_attr.rs
+
+#![warn(clippy::needless_arbitrary_self_type)]
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+mod issue_6089 {
+ // Check that we don't lint if the `self` parameter comes from expansion
+
+ macro_rules! test_from_expansion {
+ () => {
+ trait T1 {
+ fn test(self: &Self);
+ }
+
+ struct S1;
+
+ impl T1 for S1 {
+ fn test(self: &Self) {}
+ }
+ };
+ }
+
+ test_from_expansion!();
+
+ // If only the lifetime name comes from expansion we will lint, but the suggestion will have
+ // placeholders and will not be applied automatically, as we can't reliably know the original name.
+ // This specific case happened with async_trait.
+
+ trait T2 {
+ fn call_with_mut_self(&mut self);
+ }
+
+ struct S2;
+
+ // The method's signature will be expanded to:
+ // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
+ #[rename_my_lifetimes]
+ impl T2 for S2 {
+ #[allow(clippy::needless_lifetimes)]
+ fn call_with_mut_self(self: &mut Self) {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr
new file mode 100644
index 000000000..b2edbfe43
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr
@@ -0,0 +1,10 @@
+error: the type of the `self` parameter does not need to be arbitrary
+ --> $DIR/needless_arbitrary_self_type_unfixable.rs:42:31
+ |
+LL | fn call_with_mut_self(self: &mut Self) {}
+ | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self`
+ |
+ = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed
new file mode 100644
index 000000000..5e1ea663a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed
@@ -0,0 +1,40 @@
+// run-rustfix
+
+#![warn(clippy::needless_bitwise_bool)]
+
+fn returns_bool() -> bool {
+ true
+}
+
+const fn const_returns_bool() -> bool {
+ false
+}
+
+fn main() {
+ let (x, y) = (false, true);
+ if x & y {
+ println!("true")
+ }
+ if returns_bool() & x {
+ println!("true")
+ }
+ if !returns_bool() & returns_bool() {
+ println!("true")
+ }
+ if y && !x {
+ println!("true")
+ }
+
+ // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves.
+ if y & !const_returns_bool() {
+ println!("true") // This is a const function, in an UnOp
+ }
+
+ if y & "abcD".is_empty() {
+ println!("true") // This is a const method call
+ }
+
+ if y & (0 < 1) {
+ println!("true") // This is a BinOp with no side effects
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs
new file mode 100644
index 000000000..f3075fba0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs
@@ -0,0 +1,40 @@
+// run-rustfix
+
+#![warn(clippy::needless_bitwise_bool)]
+
+fn returns_bool() -> bool {
+ true
+}
+
+const fn const_returns_bool() -> bool {
+ false
+}
+
+fn main() {
+ let (x, y) = (false, true);
+ if x & y {
+ println!("true")
+ }
+ if returns_bool() & x {
+ println!("true")
+ }
+ if !returns_bool() & returns_bool() {
+ println!("true")
+ }
+ if y & !x {
+ println!("true")
+ }
+
+ // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves.
+ if y & !const_returns_bool() {
+ println!("true") // This is a const function, in an UnOp
+ }
+
+ if y & "abcD".is_empty() {
+ println!("true") // This is a const method call
+ }
+
+ if y & (0 < 1) {
+ println!("true") // This is a BinOp with no side effects
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr
new file mode 100644
index 000000000..63c88ef63
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr
@@ -0,0 +1,10 @@
+error: use of bitwise operator instead of lazy operator between booleans
+ --> $DIR/needless_bitwise_bool.rs:24:8
+ |
+LL | if y & !x {
+ | ^^^^^^ help: try: `y && !x`
+ |
+ = note: `-D clippy::needless-bitwise-bool` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
new file mode 100644
index 000000000..89dc13fd5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed
@@ -0,0 +1,126 @@
+// run-rustfix
+
+#![warn(clippy::needless_bool)]
+#![allow(
+ unused,
+ dead_code,
+ clippy::no_effect,
+ clippy::if_same_then_else,
+ clippy::equatable_if_let,
+ clippy::needless_return,
+ clippy::self_named_constructors
+)]
+
+use std::cell::Cell;
+
+macro_rules! bool_comparison_trigger {
+ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
+
+ #[derive(Clone)]
+ pub struct Trigger {
+ $($i: (Cell<bool>, bool, bool)),+
+ }
+
+ #[allow(dead_code)]
+ impl Trigger {
+ pub fn trigger(&self, key: &str) -> bool {
+ $(
+ if let stringify!($i) = key {
+ return self.$i.1 && self.$i.2 == $def;
+ }
+ )+
+ false
+ }
+ }
+ )
+}
+
+fn main() {
+ let x = true;
+ let y = false;
+ x;
+ !x;
+ !(x && y);
+ let a = 0;
+ let b = 1;
+
+ a != b;
+ a == b;
+ a >= b;
+ a > b;
+ a <= b;
+ a < b;
+ if x {
+ x
+ } else {
+ false
+ }; // would also be questionable, but we don't catch this yet
+ bool_ret3(x);
+ bool_ret4(x);
+ bool_ret5(x, x);
+ bool_ret6(x, x);
+ needless_bool(x);
+ needless_bool2(x);
+ needless_bool3(x);
+ needless_bool_condition();
+}
+
+fn bool_ret3(x: bool) -> bool {
+ return x;
+}
+
+fn bool_ret4(x: bool) -> bool {
+ return !x;
+}
+
+fn bool_ret5(x: bool, y: bool) -> bool {
+ return x && y;
+}
+
+fn bool_ret6(x: bool, y: bool) -> bool {
+ return !(x && y);
+}
+
+fn needless_bool(x: bool) {
+ if x {};
+}
+
+fn needless_bool2(x: bool) {
+ if !x {};
+}
+
+fn needless_bool3(x: bool) {
+ bool_comparison_trigger! {
+ test_one: false, false;
+ test_three: false, false;
+ test_two: true, true;
+ }
+
+ if x {};
+ if !x {};
+}
+
+fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
+ let b = false;
+ let returns_bool = || false;
+
+ let x = if b {
+ true
+ } else { !returns_bool() };
+}
+
+unsafe fn no(v: u8) -> u8 {
+ v
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn needless_bool_condition() -> bool {
+ (unsafe { no(4) } & 1 != 0);
+ let _brackets_unneeded = unsafe { no(4) } & 1 != 0;
+ fn foo() -> bool {
+ // parentheses are needed here
+ (unsafe { no(4) } & 1 != 0)
+ }
+
+ foo()
+}
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
new file mode 100644
index 000000000..c11d9472e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs
@@ -0,0 +1,186 @@
+// run-rustfix
+
+#![warn(clippy::needless_bool)]
+#![allow(
+ unused,
+ dead_code,
+ clippy::no_effect,
+ clippy::if_same_then_else,
+ clippy::equatable_if_let,
+ clippy::needless_return,
+ clippy::self_named_constructors
+)]
+
+use std::cell::Cell;
+
+macro_rules! bool_comparison_trigger {
+ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
+
+ #[derive(Clone)]
+ pub struct Trigger {
+ $($i: (Cell<bool>, bool, bool)),+
+ }
+
+ #[allow(dead_code)]
+ impl Trigger {
+ pub fn trigger(&self, key: &str) -> bool {
+ $(
+ if let stringify!($i) = key {
+ return self.$i.1 && self.$i.2 == $def;
+ }
+ )+
+ false
+ }
+ }
+ )
+}
+
+fn main() {
+ let x = true;
+ let y = false;
+ if x {
+ true
+ } else {
+ false
+ };
+ if x {
+ false
+ } else {
+ true
+ };
+ if x && y {
+ false
+ } else {
+ true
+ };
+ let a = 0;
+ let b = 1;
+
+ if a == b {
+ false
+ } else {
+ true
+ };
+ if a != b {
+ false
+ } else {
+ true
+ };
+ if a < b {
+ false
+ } else {
+ true
+ };
+ if a <= b {
+ false
+ } else {
+ true
+ };
+ if a > b {
+ false
+ } else {
+ true
+ };
+ if a >= b {
+ false
+ } else {
+ true
+ };
+ if x {
+ x
+ } else {
+ false
+ }; // would also be questionable, but we don't catch this yet
+ bool_ret3(x);
+ bool_ret4(x);
+ bool_ret5(x, x);
+ bool_ret6(x, x);
+ needless_bool(x);
+ needless_bool2(x);
+ needless_bool3(x);
+ needless_bool_condition();
+}
+
+fn bool_ret3(x: bool) -> bool {
+ if x {
+ return true;
+ } else {
+ return false;
+ };
+}
+
+fn bool_ret4(x: bool) -> bool {
+ if x {
+ return false;
+ } else {
+ return true;
+ };
+}
+
+fn bool_ret5(x: bool, y: bool) -> bool {
+ if x && y {
+ return true;
+ } else {
+ return false;
+ };
+}
+
+fn bool_ret6(x: bool, y: bool) -> bool {
+ if x && y {
+ return false;
+ } else {
+ return true;
+ };
+}
+
+fn needless_bool(x: bool) {
+ if x == true {};
+}
+
+fn needless_bool2(x: bool) {
+ if x == false {};
+}
+
+fn needless_bool3(x: bool) {
+ bool_comparison_trigger! {
+ test_one: false, false;
+ test_three: false, false;
+ test_two: true, true;
+ }
+
+ if x == true {};
+ if x == false {};
+}
+
+fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
+ let b = false;
+ let returns_bool = || false;
+
+ let x = if b {
+ true
+ } else if returns_bool() {
+ false
+ } else {
+ true
+ };
+}
+
+unsafe fn no(v: u8) -> u8 {
+ v
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn needless_bool_condition() -> bool {
+ if unsafe { no(4) } & 1 != 0 {
+ true
+ } else {
+ false
+ };
+ let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
+ fn foo() -> bool {
+ // parentheses are needed here
+ if unsafe { no(4) } & 1 != 0 { true } else { false }
+ }
+
+ foo()
+}
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
new file mode 100644
index 000000000..d2c48376f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
@@ -0,0 +1,193 @@
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:41:5
+ |
+LL | / if x {
+LL | | true
+LL | | } else {
+LL | | false
+LL | | };
+ | |_____^ help: you can reduce it to: `x`
+ |
+ = note: `-D clippy::needless-bool` implied by `-D warnings`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:46:5
+ |
+LL | / if x {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `!x`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:51:5
+ |
+LL | / if x && y {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `!(x && y)`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:59:5
+ |
+LL | / if a == b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a != b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:64:5
+ |
+LL | / if a != b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a == b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:69:5
+ |
+LL | / if a < b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a >= b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:74:5
+ |
+LL | / if a <= b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a > b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:79:5
+ |
+LL | / if a > b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a <= b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:84:5
+ |
+LL | / if a >= b {
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `a < b`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:105:5
+ |
+LL | / if x {
+LL | | return true;
+LL | | } else {
+LL | | return false;
+LL | | };
+ | |_____^ help: you can reduce it to: `return x`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:113:5
+ |
+LL | / if x {
+LL | | return false;
+LL | | } else {
+LL | | return true;
+LL | | };
+ | |_____^ help: you can reduce it to: `return !x`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:121:5
+ |
+LL | / if x && y {
+LL | | return true;
+LL | | } else {
+LL | | return false;
+LL | | };
+ | |_____^ help: you can reduce it to: `return x && y`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:129:5
+ |
+LL | / if x && y {
+LL | | return false;
+LL | | } else {
+LL | | return true;
+LL | | };
+ | |_____^ help: you can reduce it to: `return !(x && y)`
+
+error: equality checks against true are unnecessary
+ --> $DIR/fixable.rs:137:8
+ |
+LL | if x == true {};
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+ |
+ = note: `-D clippy::bool-comparison` implied by `-D warnings`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/fixable.rs:141:8
+ |
+LL | if x == false {};
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: equality checks against true are unnecessary
+ --> $DIR/fixable.rs:151:8
+ |
+LL | if x == true {};
+ | ^^^^^^^^^ help: try simplifying it as shown: `x`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/fixable.rs:152:8
+ |
+LL | if x == false {};
+ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:161:12
+ |
+LL | } else if returns_bool() {
+ | ____________^
+LL | | false
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^ help: you can reduce it to: `{ !returns_bool() }`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:174:5
+ |
+LL | / if unsafe { no(4) } & 1 != 0 {
+LL | | true
+LL | | } else {
+LL | | false
+LL | | };
+ | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:179:30
+ |
+LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0`
+
+error: this if-then-else expression returns a bool literal
+ --> $DIR/fixable.rs:182:9
+ |
+LL | if unsafe { no(4) } & 1 != 0 { true } else { false }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs
new file mode 100644
index 000000000..588bb88f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs
@@ -0,0 +1,47 @@
+#![warn(clippy::needless_bool)]
+#![allow(
+ unused,
+ dead_code,
+ clippy::no_effect,
+ clippy::if_same_then_else,
+ clippy::needless_return,
+ clippy::branches_sharing_code
+)]
+
+fn main() {
+ let x = true;
+ let y = false;
+ if x {
+ true
+ } else {
+ true
+ };
+ if x {
+ false
+ } else {
+ false
+ };
+ if x {
+ x
+ } else {
+ false
+ }; // would also be questionable, but we don't catch this yet
+ bool_ret(x);
+ bool_ret2(x);
+}
+
+fn bool_ret(x: bool) -> bool {
+ if x {
+ return true;
+ } else {
+ return true;
+ };
+}
+
+fn bool_ret2(x: bool) -> bool {
+ if x {
+ return false;
+ } else {
+ return false;
+ };
+}
diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr
new file mode 100644
index 000000000..0ccc9416b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr
@@ -0,0 +1,44 @@
+error: this if-then-else expression will always return true
+ --> $DIR/simple.rs:14:5
+ |
+LL | / if x {
+LL | | true
+LL | | } else {
+LL | | true
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::needless-bool` implied by `-D warnings`
+
+error: this if-then-else expression will always return false
+ --> $DIR/simple.rs:19:5
+ |
+LL | / if x {
+LL | | false
+LL | | } else {
+LL | | false
+LL | | };
+ | |_____^
+
+error: this if-then-else expression will always return true
+ --> $DIR/simple.rs:34:5
+ |
+LL | / if x {
+LL | | return true;
+LL | | } else {
+LL | | return true;
+LL | | };
+ | |_____^
+
+error: this if-then-else expression will always return false
+ --> $DIR/simple.rs:42:5
+ |
+LL | / if x {
+LL | | return false;
+LL | | } else {
+LL | | return false;
+LL | | };
+ | |_____^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
new file mode 100644
index 000000000..bfd2725ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -0,0 +1,185 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+
+#[warn(clippy::all, clippy::needless_borrow)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
+fn main() {
+ let a = 5;
+ let ref_a = &a;
+ let _ = x(&a); // no warning
+ let _ = x(&a); // warn
+
+ let mut b = 5;
+ mut_ref(&mut b); // no warning
+ mut_ref(&mut b); // warn
+
+ let s = &String::from("hi");
+ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
+ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
+ let vec = Vec::new();
+ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
+ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
+ let garbl = match 42 {
+ 44 => &a,
+ 45 => {
+ println!("foo");
+ &a
+ },
+ 46 => &a,
+ 47 => {
+ println!("foo");
+ loop {
+ println!("{}", a);
+ if a == 25 {
+ break ref_a;
+ }
+ }
+ },
+ _ => panic!(),
+ };
+
+ let _ = x(&a);
+ let _ = x(&a);
+ let _ = x(&mut b);
+ let _ = x(ref_a);
+ {
+ let b = &mut b;
+ x(b);
+ }
+
+ // Issue #8191
+ let mut x = 5;
+ let mut x = &mut x;
+
+ mut_ref(x);
+ mut_ref(x);
+ let y: &mut i32 = x;
+ let y: &mut i32 = x;
+
+ let y = match 0 {
+ // Don't lint. Removing the borrow would move 'x'
+ 0 => &mut x,
+ _ => &mut *x,
+ };
+ let y: &mut i32 = match 0 {
+ // Lint here. The type given above triggers auto-borrow.
+ 0 => x,
+ _ => &mut *x,
+ };
+ fn ref_mut_i32(_: &mut i32) {}
+ ref_mut_i32(match 0 {
+ // Lint here. The type given above triggers auto-borrow.
+ 0 => x,
+ _ => &mut *x,
+ });
+ // use 'x' after to make sure it's still usable in the fixed code.
+ *x = 5;
+
+ let s = String::new();
+ // let _ = (&s).len();
+ // let _ = (&s).capacity();
+ // let _ = (&&s).capacity();
+
+ let x = (1, 2);
+ let _ = x.0;
+ let x = &x as *const (i32, i32);
+ let _ = unsafe { (*x).0 };
+
+ // Issue #8367
+ trait Foo {
+ fn foo(self);
+ }
+ impl Foo for &'_ () {
+ fn foo(self) {}
+ }
+ (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
+ (&()).foo();
+
+ impl Foo for i32 {
+ fn foo(self) {}
+ }
+ impl Foo for &'_ i32 {
+ fn foo(self) {}
+ }
+ (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
+ (&5).foo();
+
+ trait FooRef {
+ fn foo_ref(&self);
+ }
+ impl FooRef for () {
+ fn foo_ref(&self) {}
+ }
+ impl FooRef for &'_ () {
+ fn foo_ref(&self) {}
+ }
+ (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+ struct S;
+ impl From<S> for u32 {
+ fn from(s: S) -> Self {
+ (&s).into()
+ }
+ }
+ impl From<&S> for u32 {
+ fn from(s: &S) -> Self {
+ 0
+ }
+ }
+}
+
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+ *y
+}
+
+fn mut_ref(y: &mut i32) {
+ *y = 5;
+}
+
+fn f<T: Copy>(y: &T) -> T {
+ *y
+}
+
+fn g(y: &[u8]) -> u8 {
+ y[0]
+}
+
+trait Trait {}
+
+impl<'a> Trait for &'a str {}
+
+fn h(_: &dyn Trait) {}
+
+#[allow(dead_code)]
+fn check_expect_suppression() {
+ let a = 5;
+ #[expect(clippy::needless_borrow)]
+ let _ = x(&&a);
+}
+
+#[allow(dead_code)]
+mod issue9160 {
+ pub struct S<F> {
+ f: F,
+ }
+
+ impl<T, F> S<F>
+ where
+ F: Fn() -> T,
+ {
+ fn calls_field(&self) -> T {
+ (self.f)()
+ }
+ }
+
+ impl<T, F> S<F>
+ where
+ F: FnMut() -> T,
+ {
+ fn calls_mut_field(&mut self) -> T {
+ (self.f)()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
new file mode 100644
index 000000000..c457d8c54
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -0,0 +1,185 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+
+#[warn(clippy::all, clippy::needless_borrow)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
+fn main() {
+ let a = 5;
+ let ref_a = &a;
+ let _ = x(&a); // no warning
+ let _ = x(&&a); // warn
+
+ let mut b = 5;
+ mut_ref(&mut b); // no warning
+ mut_ref(&mut &mut b); // warn
+
+ let s = &String::from("hi");
+ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
+ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
+ let vec = Vec::new();
+ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
+ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
+ let garbl = match 42 {
+ 44 => &a,
+ 45 => {
+ println!("foo");
+ &&a
+ },
+ 46 => &&a,
+ 47 => {
+ println!("foo");
+ loop {
+ println!("{}", a);
+ if a == 25 {
+ break &ref_a;
+ }
+ }
+ },
+ _ => panic!(),
+ };
+
+ let _ = x(&&&a);
+ let _ = x(&mut &&a);
+ let _ = x(&&&mut b);
+ let _ = x(&&ref_a);
+ {
+ let b = &mut b;
+ x(&b);
+ }
+
+ // Issue #8191
+ let mut x = 5;
+ let mut x = &mut x;
+
+ mut_ref(&mut x);
+ mut_ref(&mut &mut x);
+ let y: &mut i32 = &mut x;
+ let y: &mut i32 = &mut &mut x;
+
+ let y = match 0 {
+ // Don't lint. Removing the borrow would move 'x'
+ 0 => &mut x,
+ _ => &mut *x,
+ };
+ let y: &mut i32 = match 0 {
+ // Lint here. The type given above triggers auto-borrow.
+ 0 => &mut x,
+ _ => &mut *x,
+ };
+ fn ref_mut_i32(_: &mut i32) {}
+ ref_mut_i32(match 0 {
+ // Lint here. The type given above triggers auto-borrow.
+ 0 => &mut x,
+ _ => &mut *x,
+ });
+ // use 'x' after to make sure it's still usable in the fixed code.
+ *x = 5;
+
+ let s = String::new();
+ // let _ = (&s).len();
+ // let _ = (&s).capacity();
+ // let _ = (&&s).capacity();
+
+ let x = (1, 2);
+ let _ = (&x).0;
+ let x = &x as *const (i32, i32);
+ let _ = unsafe { (&*x).0 };
+
+ // Issue #8367
+ trait Foo {
+ fn foo(self);
+ }
+ impl Foo for &'_ () {
+ fn foo(self) {}
+ }
+ (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
+ (&&()).foo();
+
+ impl Foo for i32 {
+ fn foo(self) {}
+ }
+ impl Foo for &'_ i32 {
+ fn foo(self) {}
+ }
+ (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
+ (&&5).foo();
+
+ trait FooRef {
+ fn foo_ref(&self);
+ }
+ impl FooRef for () {
+ fn foo_ref(&self) {}
+ }
+ impl FooRef for &'_ () {
+ fn foo_ref(&self) {}
+ }
+ (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+ struct S;
+ impl From<S> for u32 {
+ fn from(s: S) -> Self {
+ (&s).into()
+ }
+ }
+ impl From<&S> for u32 {
+ fn from(s: &S) -> Self {
+ 0
+ }
+ }
+}
+
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+ *y
+}
+
+fn mut_ref(y: &mut i32) {
+ *y = 5;
+}
+
+fn f<T: Copy>(y: &T) -> T {
+ *y
+}
+
+fn g(y: &[u8]) -> u8 {
+ y[0]
+}
+
+trait Trait {}
+
+impl<'a> Trait for &'a str {}
+
+fn h(_: &dyn Trait) {}
+
+#[allow(dead_code)]
+fn check_expect_suppression() {
+ let a = 5;
+ #[expect(clippy::needless_borrow)]
+ let _ = x(&&a);
+}
+
+#[allow(dead_code)]
+mod issue9160 {
+ pub struct S<F> {
+ f: F,
+ }
+
+ impl<T, F> S<F>
+ where
+ F: Fn() -> T,
+ {
+ fn calls_field(&self) -> T {
+ (&self.f)()
+ }
+ }
+
+ impl<T, F> S<F>
+ where
+ F: FnMut() -> T,
+ {
+ fn calls_mut_field(&mut self) -> T {
+ (&mut self.f)()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
new file mode 100644
index 000000000..66588689d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -0,0 +1,136 @@
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:11:15
+ |
+LL | let _ = x(&&a); // warn
+ | ^^^ help: change this to: `&a`
+ |
+ = note: `-D clippy::needless-borrow` implied by `-D warnings`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:15:13
+ |
+LL | mut_ref(&mut &mut b); // warn
+ | ^^^^^^^^^^^ help: change this to: `&mut b`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:27:13
+ |
+LL | &&a
+ | ^^^ help: change this to: `&a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:29:15
+ |
+LL | 46 => &&a,
+ | ^^^ help: change this to: `&a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:35:27
+ |
+LL | break &ref_a;
+ | ^^^^^^ help: change this to: `ref_a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:42:15
+ |
+LL | let _ = x(&&&a);
+ | ^^^^ help: change this to: `&a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:43:15
+ |
+LL | let _ = x(&mut &&a);
+ | ^^^^^^^^ help: change this to: `&a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:44:15
+ |
+LL | let _ = x(&&&mut b);
+ | ^^^^^^^^ help: change this to: `&mut b`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:45:15
+ |
+LL | let _ = x(&&ref_a);
+ | ^^^^^^^ help: change this to: `ref_a`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:48:11
+ |
+LL | x(&b);
+ | ^^ help: change this to: `b`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:55:13
+ |
+LL | mut_ref(&mut x);
+ | ^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:56:13
+ |
+LL | mut_ref(&mut &mut x);
+ | ^^^^^^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:57:23
+ |
+LL | let y: &mut i32 = &mut x;
+ | ^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:58:23
+ |
+LL | let y: &mut i32 = &mut &mut x;
+ | ^^^^^^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:67:14
+ |
+LL | 0 => &mut x,
+ | ^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:73:14
+ |
+LL | 0 => &mut x,
+ | ^^^^^^ help: change this to: `x`
+
+error: this expression borrows a value the compiler would automatically borrow
+ --> $DIR/needless_borrow.rs:85:13
+ |
+LL | let _ = (&x).0;
+ | ^^^^ help: change this to: `x`
+
+error: this expression borrows a value the compiler would automatically borrow
+ --> $DIR/needless_borrow.rs:87:22
+ |
+LL | let _ = unsafe { (&*x).0 };
+ | ^^^^^ help: change this to: `(*x)`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:97:5
+ |
+LL | (&&()).foo();
+ | ^^^^^^ help: change this to: `(&())`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:106:5
+ |
+LL | (&&5).foo();
+ | ^^^^^ help: change this to: `(&5)`
+
+error: this expression borrows a value the compiler would automatically borrow
+ --> $DIR/needless_borrow.rs:173:13
+ |
+LL | (&self.f)()
+ | ^^^^^^^^^ help: change this to: `(self.f)`
+
+error: this expression borrows a value the compiler would automatically borrow
+ --> $DIR/needless_borrow.rs:182:13
+ |
+LL | (&mut self.f)()
+ | ^^^^^^^^^^^^^ help: change this to: `(self.f)`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.rs b/src/tools/clippy/tests/ui/needless_borrow_pat.rs
new file mode 100644
index 000000000..222e8e617
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrow_pat.rs
@@ -0,0 +1,150 @@
+// FIXME: run-rustfix waiting on multi-span suggestions
+
+#![warn(clippy::needless_borrow)]
+#![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)]
+
+fn f1(_: &str) {}
+macro_rules! m1 {
+ ($e:expr) => {
+ f1($e)
+ };
+}
+macro_rules! m3 {
+ ($i:ident) => {
+ Some(ref $i)
+ };
+}
+macro_rules! if_chain {
+ (if $e:expr; $($rest:tt)*) => {
+ if $e {
+ if_chain!($($rest)*)
+ }
+ };
+
+ (if let $p:pat = $e:expr; $($rest:tt)*) => {
+ if let $p = $e {
+ if_chain!($($rest)*)
+ }
+ };
+
+ (then $b:block) => {
+ $b
+ };
+}
+
+#[allow(dead_code)]
+fn main() {
+ let x = String::new();
+
+ // Ok, reference to a String.
+ let _: &String = match Some(x.clone()) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Ok, reference to a &mut String
+ let _: &&mut String = match Some(&mut x.clone()) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Ok, the pattern is from a macro
+ let _: &String = match Some(&x) {
+ m3!(x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &String = match Some(&x) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String.
+ let _: &String = match Some(&x) {
+ Some(ref x) => *x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &String = match Some(&x) {
+ Some(ref x) => {
+ f1(x);
+ f1(*x);
+ x
+ },
+ None => return,
+ };
+
+ // Err, reference to a &String
+ match Some(&x) {
+ Some(ref x) => m1!(x),
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _ = |&ref x: &&String| {
+ let _: &String = x;
+ };
+
+ // Err, reference to a &String
+ let (ref y,) = (&x,);
+ let _: &String = *y;
+
+ let y = &&x;
+ // Ok, different y
+ let _: &String = *y;
+
+ let x = (0, 0);
+ // Err, reference to a &u32. Don't suggest adding a reference to the field access.
+ let _: u32 = match Some(&x) {
+ Some(ref x) => x.0,
+ None => return,
+ };
+
+ enum E {
+ A(&'static u32),
+ B(&'static u32),
+ }
+ // Err, reference to &u32.
+ let _: &u32 = match E::A(&0) {
+ E::A(ref x) | E::B(ref x) => *x,
+ };
+
+ // Err, reference to &String.
+ if_chain! {
+ if true;
+ if let Some(ref x) = Some(&String::new());
+ then {
+ f1(x);
+ }
+ }
+}
+
+// Err, reference to a &String
+fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ let _: &String = x;
+ *x
+}
+
+trait T1 {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &String = x;
+ }
+}
+
+struct S;
+impl T1 for S {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &String = *x;
+ }
+}
+
+// Ok - used to error due to rustc bug
+#[allow(dead_code)]
+#[derive(Debug)]
+enum Foo<'a> {
+ Str(&'a str),
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.stderr b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr
new file mode 100644
index 000000000..db3b52b88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrow_pat.stderr
@@ -0,0 +1,112 @@
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:59:14
+ |
+LL | Some(ref x) => x,
+ | ^^^^^ help: try this: `x`
+ |
+ = note: `-D clippy::needless-borrow` implied by `-D warnings`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:65:14
+ |
+LL | Some(ref x) => *x,
+ | ^^^^^
+ |
+help: try this
+ |
+LL | Some(x) => x,
+ | ~ ~
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:71:14
+ |
+LL | Some(ref x) => {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ Some(x) => {
+LL | f1(x);
+LL ~ f1(x);
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:81:14
+ |
+LL | Some(ref x) => m1!(x),
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:86:15
+ |
+LL | let _ = |&ref x: &&String| {
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:91:10
+ |
+LL | let (ref y,) = (&x,);
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ let (y,) = (&x,);
+LL ~ let _: &String = y;
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:101:14
+ |
+LL | Some(ref x) => x.0,
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:111:14
+ |
+LL | E::A(ref x) | E::B(ref x) => *x,
+ | ^^^^^ ^^^^^
+ |
+help: try this
+ |
+LL | E::A(x) | E::B(x) => x,
+ | ~ ~ ~
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:117:21
+ |
+LL | if let Some(ref x) = Some(&String::new());
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:125:12
+ |
+LL | fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f2<'a>(&x: &&'a String) -> &'a String {
+LL | let _: &String = x;
+LL ~ x
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:132:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^ help: try this: `x`
+
+error: this pattern creates a reference to a reference
+ --> $DIR/needless_borrow_pat.rs:140:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &String = x;
+ |
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
new file mode 100644
index 000000000..a0937a2c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+
+#[warn(clippy::needless_borrowed_reference)]
+#[allow(unused_variables)]
+fn main() {
+ let mut v = Vec::<String>::new();
+ let _ = v.iter_mut().filter(|a| a.is_empty());
+ // ^ should be linted
+
+ let var = 3;
+ let thingy = Some(&var);
+ if let Some(&ref v) = thingy {
+ // ^ should be linted
+ }
+
+ let mut var2 = 5;
+ let thingy2 = Some(&mut var2);
+ if let Some(&mut ref mut v) = thingy2 {
+ // ^ should **not** be linted
+ // v is borrowed as mutable.
+ *v = 10;
+ }
+ if let Some(&mut ref v) = thingy2 {
+ // ^ should **not** be linted
+ // here, v is borrowed as immutable.
+ // can't do that:
+ //*v = 15;
+ }
+}
+
+#[allow(dead_code)]
+enum Animal {
+ Cat(u64),
+ Dog(u64),
+}
+
+#[allow(unused_variables)]
+#[allow(dead_code)]
+fn foo(a: &Animal, b: &Animal) {
+ match (a, b) {
+ (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
+ // ^ and ^ should **not** be linted
+ (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs
new file mode 100644
index 000000000..500ac448f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+
+#[warn(clippy::needless_borrowed_reference)]
+#[allow(unused_variables)]
+fn main() {
+ let mut v = Vec::<String>::new();
+ let _ = v.iter_mut().filter(|&ref a| a.is_empty());
+ // ^ should be linted
+
+ let var = 3;
+ let thingy = Some(&var);
+ if let Some(&ref v) = thingy {
+ // ^ should be linted
+ }
+
+ let mut var2 = 5;
+ let thingy2 = Some(&mut var2);
+ if let Some(&mut ref mut v) = thingy2 {
+ // ^ should **not** be linted
+ // v is borrowed as mutable.
+ *v = 10;
+ }
+ if let Some(&mut ref v) = thingy2 {
+ // ^ should **not** be linted
+ // here, v is borrowed as immutable.
+ // can't do that:
+ //*v = 15;
+ }
+}
+
+#[allow(dead_code)]
+enum Animal {
+ Cat(u64),
+ Dog(u64),
+}
+
+#[allow(unused_variables)]
+#[allow(dead_code)]
+fn foo(a: &Animal, b: &Animal) {
+ match (a, b) {
+ (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
+ // ^ and ^ should **not** be linted
+ (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
new file mode 100644
index 000000000..0a5cfb3db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
@@ -0,0 +1,10 @@
+error: this pattern takes a reference on something that is being de-referenced
+ --> $DIR/needless_borrowed_ref.rs:7:34
+ |
+LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty());
+ | ^^^^^^ help: try removing the `&ref` part and just keep: `a`
+ |
+ = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed
new file mode 100644
index 000000000..6ecbbcb62
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_collect.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+
+#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
+
+#[warn(clippy::needless_collect)]
+#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
+fn main() {
+ let sample = [1; 5];
+ let len = sample.iter().count();
+ if sample.iter().next().is_none() {
+ // Empty
+ }
+ sample.iter().cloned().any(|x| x == 1);
+ // #7164 HashMap's and BTreeMap's `len` usage should not be linted
+ sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
+ sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len();
+
+ sample.iter().map(|x| (x, x)).next().is_none();
+ sample.iter().map(|x| (x, x)).next().is_none();
+
+ // Notice the `HashSet`--this should not be linted
+ sample.iter().collect::<HashSet<_>>().len();
+ // Neither should this
+ sample.iter().collect::<BTreeSet<_>>().len();
+
+ sample.iter().count();
+ sample.iter().next().is_none();
+ sample.iter().cloned().any(|x| x == 1);
+ sample.iter().any(|x| x == &1);
+
+ // `BinaryHeap` doesn't have `contains` method
+ sample.iter().count();
+ sample.iter().next().is_none();
+}
diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs
new file mode 100644
index 000000000..8dc69bcf5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_collect.rs
@@ -0,0 +1,36 @@
+// run-rustfix
+
+#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
+
+#[warn(clippy::needless_collect)]
+#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
+fn main() {
+ let sample = [1; 5];
+ let len = sample.iter().collect::<Vec<_>>().len();
+ if sample.iter().collect::<Vec<_>>().is_empty() {
+ // Empty
+ }
+ sample.iter().cloned().collect::<Vec<_>>().contains(&1);
+ // #7164 HashMap's and BTreeMap's `len` usage should not be linted
+ sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
+ sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len();
+
+ sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty();
+ sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty();
+
+ // Notice the `HashSet`--this should not be linted
+ sample.iter().collect::<HashSet<_>>().len();
+ // Neither should this
+ sample.iter().collect::<BTreeSet<_>>().len();
+
+ sample.iter().collect::<LinkedList<_>>().len();
+ sample.iter().collect::<LinkedList<_>>().is_empty();
+ sample.iter().cloned().collect::<LinkedList<_>>().contains(&1);
+ sample.iter().collect::<LinkedList<_>>().contains(&&1);
+
+ // `BinaryHeap` doesn't have `contains` method
+ sample.iter().collect::<BinaryHeap<_>>().len();
+ sample.iter().collect::<BinaryHeap<_>>().is_empty();
+}
diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr
new file mode 100644
index 000000000..039091627
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_collect.stderr
@@ -0,0 +1,70 @@
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:11:29
+ |
+LL | let len = sample.iter().collect::<Vec<_>>().len();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
+ |
+ = note: `-D clippy::needless-collect` implied by `-D warnings`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:12:22
+ |
+LL | if sample.iter().collect::<Vec<_>>().is_empty() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:15:28
+ |
+LL | sample.iter().cloned().collect::<Vec<_>>().contains(&1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:20:35
+ |
+LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:21:35
+ |
+LL | sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:28:19
+ |
+LL | sample.iter().collect::<LinkedList<_>>().len();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:29:19
+ |
+LL | sample.iter().collect::<LinkedList<_>>().is_empty();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:30:28
+ |
+LL | sample.iter().cloned().collect::<LinkedList<_>>().contains(&1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:31:19
+ |
+LL | sample.iter().collect::<LinkedList<_>>().contains(&&1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:34:19
+ |
+LL | sample.iter().collect::<BinaryHeap<_>>().len();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:35:19
+ |
+LL | sample.iter().collect::<BinaryHeap<_>>().is_empty();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
new file mode 100644
index 000000000..1f11d1f8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
@@ -0,0 +1,114 @@
+use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
+
+fn main() {
+ let sample = [1; 5];
+ let indirect_iter = sample.iter().collect::<Vec<_>>();
+ indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+ let indirect_len = sample.iter().collect::<VecDeque<_>>();
+ indirect_len.len();
+ let indirect_empty = sample.iter().collect::<VecDeque<_>>();
+ indirect_empty.is_empty();
+ let indirect_contains = sample.iter().collect::<VecDeque<_>>();
+ indirect_contains.contains(&&5);
+ let indirect_negative = sample.iter().collect::<Vec<_>>();
+ indirect_negative.len();
+ indirect_negative
+ .into_iter()
+ .map(|x| (*x, *x + 1))
+ .collect::<HashMap<_, _>>();
+
+ // #6202
+ let a = "a".to_string();
+ let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
+ let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+ non_copy_contains.contains(&a);
+
+ // Fix #5991
+ let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ let vec_b = vec_a.iter().collect::<Vec<_>>();
+ if vec_b.len() > 3 {}
+ let other_vec = vec![1, 3, 12, 4, 16, 2];
+ let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::<Vec<_>>();
+
+ // Fix #6297
+ let sample = [1; 5];
+ let multiple_indirect = sample.iter().collect::<Vec<_>>();
+ let sample2 = vec![2, 3];
+ if multiple_indirect.is_empty() {
+ // do something
+ } else {
+ let found = sample2
+ .iter()
+ .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0))
+ .collect::<Vec<_>>();
+ }
+}
+
+mod issue7110 {
+ // #7110 - lint for type annotation cases
+ use super::*;
+
+ fn lint_vec(string: &str) -> usize {
+ let buffer: Vec<&str> = string.split('/').collect();
+ buffer.len()
+ }
+ fn lint_vec_deque() -> usize {
+ let sample = [1; 5];
+ let indirect_len: VecDeque<_> = sample.iter().collect();
+ indirect_len.len()
+ }
+ fn lint_linked_list() -> usize {
+ let sample = [1; 5];
+ let indirect_len: LinkedList<_> = sample.iter().collect();
+ indirect_len.len()
+ }
+ fn lint_binary_heap() -> usize {
+ let sample = [1; 5];
+ let indirect_len: BinaryHeap<_> = sample.iter().collect();
+ indirect_len.len()
+ }
+ fn dont_lint(string: &str) -> usize {
+ let buffer: Vec<&str> = string.split('/').collect();
+ for buff in &buffer {
+ println!("{}", buff);
+ }
+ buffer.len()
+ }
+}
+
+mod issue7975 {
+ use super::*;
+
+ fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
+ }
+
+ fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ let iter = collected_vec.into_iter();
+ iter.map(|_| mut_ref.push(())).collect()
+ }
+
+ fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ let iter = collected_vec.into_iter();
+ let mapped_iter = iter.map(|_| mut_ref.push(()));
+ mapped_iter.collect()
+ }
+}
+
+fn allow_test() {
+ #[allow(clippy::needless_collect)]
+ let v = [1].iter().collect::<Vec<_>>();
+ v.into_iter().collect::<HashSet<_>>();
+}
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
new file mode 100644
index 000000000..0f5e78f91
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
@@ -0,0 +1,129 @@
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:5:39
+ |
+LL | let indirect_iter = sample.iter().collect::<Vec<_>>();
+ | ^^^^^^^
+LL | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+ | ------------------------- the iterator could be used here instead
+ |
+ = note: `-D clippy::needless-collect` implied by `-D warnings`
+help: use the original Iterator instead of collecting it and then producing a new one
+ |
+LL ~
+LL ~ sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:7:38
+ |
+LL | let indirect_len = sample.iter().collect::<VecDeque<_>>();
+ | ^^^^^^^
+LL | indirect_len.len();
+ | ------------------ the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL ~ sample.iter().count();
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:9:40
+ |
+LL | let indirect_empty = sample.iter().collect::<VecDeque<_>>();
+ | ^^^^^^^
+LL | indirect_empty.is_empty();
+ | ------------------------- the iterator could be used here instead
+ |
+help: check if the original Iterator has anything instead of collecting it and seeing if it's empty
+ |
+LL ~
+LL ~ sample.iter().next().is_none();
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:11:43
+ |
+LL | let indirect_contains = sample.iter().collect::<VecDeque<_>>();
+ | ^^^^^^^
+LL | indirect_contains.contains(&&5);
+ | ------------------------------- the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL ~ sample.iter().any(|x| x == &5);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:23:48
+ |
+LL | let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+ | ^^^^^^^
+LL | non_copy_contains.contains(&a);
+ | ------------------------------ the iterator could be used here instead
+ |
+help: check if the original Iterator contains an element instead of collecting then checking
+ |
+LL ~
+LL ~ sample.into_iter().any(|x| x == a);
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:52:51
+ |
+LL | let buffer: Vec<&str> = string.split('/').collect();
+ | ^^^^^^^
+LL | buffer.len()
+ | ------------ the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL ~ string.split('/').count()
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:57:55
+ |
+LL | let indirect_len: VecDeque<_> = sample.iter().collect();
+ | ^^^^^^^
+LL | indirect_len.len()
+ | ------------------ the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL ~ sample.iter().count()
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:62:57
+ |
+LL | let indirect_len: LinkedList<_> = sample.iter().collect();
+ | ^^^^^^^
+LL | indirect_len.len()
+ | ------------------ the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL ~ sample.iter().count()
+ |
+
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:67:57
+ |
+LL | let indirect_len: BinaryHeap<_> = sample.iter().collect();
+ | ^^^^^^^
+LL | indirect_len.len()
+ | ------------------ the iterator could be used here instead
+ |
+help: take the original Iterator's count instead of collecting it and finding the length
+ |
+LL ~
+LL ~ sample.iter().count()
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs
new file mode 100644
index 000000000..f105d3d65
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_continue.rs
@@ -0,0 +1,144 @@
+#![warn(clippy::needless_continue)]
+
+macro_rules! zero {
+ ($x:expr) => {
+ $x == 0
+ };
+}
+
+macro_rules! nonzero {
+ ($x:expr) => {
+ !zero!($x)
+ };
+}
+
+#[allow(clippy::nonminimal_bool)]
+fn main() {
+ let mut i = 1;
+ while i < 10 {
+ i += 1;
+
+ if i % 2 == 0 && i % 3 == 0 {
+ println!("{}", i);
+ println!("{}", i + 1);
+ if i % 5 == 0 {
+ println!("{}", i + 2);
+ }
+ let i = 0;
+ println!("bar {} ", i);
+ } else {
+ continue;
+ }
+
+ println!("bleh");
+ {
+ println!("blah");
+ }
+
+ // some comments that also should ideally be included in the
+ // output of the lint suggestion if possible.
+ if !(!(i == 2) || !(i == 5)) {
+ println!("lama");
+ }
+
+ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
+ continue;
+ } else {
+ println!("Blabber");
+ println!("Jabber");
+ }
+
+ println!("bleh");
+ }
+}
+
+fn simple_loop() {
+ loop {
+ continue; // should lint here
+ }
+}
+
+fn simple_loop2() {
+ loop {
+ println!("bleh");
+ continue; // should lint here
+ }
+}
+
+#[rustfmt::skip]
+fn simple_loop3() {
+ loop {
+ continue // should lint here
+ }
+}
+
+#[rustfmt::skip]
+fn simple_loop4() {
+ loop {
+ println!("bleh");
+ continue // should lint here
+ }
+}
+
+mod issue_2329 {
+ fn condition() -> bool {
+ unimplemented!()
+ }
+ fn update_condition() {}
+
+ // only the outer loop has a label
+ fn foo() {
+ 'outer: loop {
+ println!("Entry");
+ while condition() {
+ update_condition();
+ if condition() {
+ println!("foo-1");
+ } else {
+ continue 'outer; // should not lint here
+ }
+ println!("foo-2");
+
+ update_condition();
+ if condition() {
+ continue 'outer; // should not lint here
+ } else {
+ println!("foo-3");
+ }
+ println!("foo-4");
+ }
+ }
+ }
+
+ // both loops have labels
+ fn bar() {
+ 'outer: loop {
+ println!("Entry");
+ 'inner: while condition() {
+ update_condition();
+ if condition() {
+ println!("bar-1");
+ } else {
+ continue 'outer; // should not lint here
+ }
+ println!("bar-2");
+
+ update_condition();
+ if condition() {
+ println!("bar-3");
+ } else {
+ continue 'inner; // should lint here
+ }
+ println!("bar-4");
+
+ update_condition();
+ if condition() {
+ continue; // should lint here
+ } else {
+ println!("bar-5");
+ }
+ println!("bar-6");
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr
new file mode 100644
index 000000000..b8657c74c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_continue.stderr
@@ -0,0 +1,131 @@
+error: this `else` block is redundant
+ --> $DIR/needless_continue.rs:29:16
+ |
+LL | } else {
+ | ________________^
+LL | | continue;
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::needless-continue` implied by `-D warnings`
+ = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
+ if i % 2 == 0 && i % 3 == 0 {
+ println!("{}", i);
+ println!("{}", i + 1);
+ if i % 5 == 0 {
+ println!("{}", i + 2);
+ }
+ let i = 0;
+ println!("bar {} ", i);
+ // merged code follows:
+ println!("bleh");
+ {
+ println!("blah");
+ }
+ if !(!(i == 2) || !(i == 5)) {
+ println!("lama");
+ }
+ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
+ continue;
+ } else {
+ println!("Blabber");
+ println!("Jabber");
+ }
+ println!("bleh");
+ }
+
+error: there is no need for an explicit `else` block for this `if` expression
+ --> $DIR/needless_continue.rs:44:9
+ |
+LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
+LL | | continue;
+LL | | } else {
+LL | | println!("Blabber");
+LL | | println!("Jabber");
+LL | | }
+ | |_________^
+ |
+ = help: consider dropping the `else` clause
+ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
+ continue;
+ }
+ {
+ println!("Blabber");
+ println!("Jabber");
+ }
+
+error: this `continue` expression is redundant
+ --> $DIR/needless_continue.rs:57:9
+ |
+LL | continue; // should lint here
+ | ^^^^^^^^^
+ |
+ = help: consider dropping the `continue` expression
+
+error: this `continue` expression is redundant
+ --> $DIR/needless_continue.rs:64:9
+ |
+LL | continue; // should lint here
+ | ^^^^^^^^^
+ |
+ = help: consider dropping the `continue` expression
+
+error: this `continue` expression is redundant
+ --> $DIR/needless_continue.rs:71:9
+ |
+LL | continue // should lint here
+ | ^^^^^^^^
+ |
+ = help: consider dropping the `continue` expression
+
+error: this `continue` expression is redundant
+ --> $DIR/needless_continue.rs:79:9
+ |
+LL | continue // should lint here
+ | ^^^^^^^^
+ |
+ = help: consider dropping the `continue` expression
+
+error: this `else` block is redundant
+ --> $DIR/needless_continue.rs:129:24
+ |
+LL | } else {
+ | ________________________^
+LL | | continue 'inner; // should lint here
+LL | | }
+ | |_________________^
+ |
+ = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
+ if condition() {
+ println!("bar-3");
+ // merged code follows:
+ println!("bar-4");
+ update_condition();
+ if condition() {
+ continue; // should lint here
+ } else {
+ println!("bar-5");
+ }
+ println!("bar-6");
+ }
+
+error: there is no need for an explicit `else` block for this `if` expression
+ --> $DIR/needless_continue.rs:135:17
+ |
+LL | / if condition() {
+LL | | continue; // should lint here
+LL | | } else {
+LL | | println!("bar-5");
+LL | | }
+ | |_________________^
+ |
+ = help: consider dropping the `else` clause
+ if condition() {
+ continue; // should lint here
+ }
+ {
+ println!("bar-5");
+ }
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs
new file mode 100644
index 000000000..83e9bbaa3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_doc_main.rs
@@ -0,0 +1,140 @@
+/// This is a test for needless `fn main()` in doctests.
+///
+/// # Examples
+///
+/// This should lint
+/// ```
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// With an explicit return type it should lint too
+/// ```edition2015
+/// fn main() -> () {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// This should, too.
+/// ```rust
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// This one too.
+/// ```no_run
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+fn bad_doctests() {}
+
+/// # Examples
+///
+/// This shouldn't lint, because the `main` is empty:
+/// ```
+/// fn main(){}
+/// ```
+///
+/// This shouldn't lint either, because main is async:
+/// ```edition2018
+/// async fn main() {
+/// assert_eq!(42, ANSWER);
+/// }
+/// ```
+///
+/// Same here, because the return type is not the unit type:
+/// ```
+/// fn main() -> Result<()> {
+/// Ok(())
+/// }
+/// ```
+///
+/// This shouldn't lint either, because there's a `static`:
+/// ```
+/// static ANSWER: i32 = 42;
+///
+/// fn main() {
+/// assert_eq!(42, ANSWER);
+/// }
+/// ```
+///
+/// This shouldn't lint either, because there's a `const`:
+/// ```
+/// fn main() {
+/// assert_eq!(42, ANSWER);
+/// }
+///
+/// const ANSWER: i32 = 42;
+/// ```
+///
+/// Neither should this lint because of `extern crate`:
+/// ```
+/// #![feature(test)]
+/// extern crate test;
+/// fn main() {
+/// assert_eq(1u8, test::black_box(1));
+/// }
+/// ```
+///
+/// Neither should this lint because it has an extern block:
+/// ```
+/// extern {}
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// This should not lint because there is another function defined:
+/// ```
+/// fn fun() {}
+///
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// We should not lint inside raw strings ...
+/// ```
+/// let string = r#"
+/// fn main() {
+/// unimplemented!();
+/// }
+/// "#;
+/// ```
+///
+/// ... or comments
+/// ```
+/// // fn main() {
+/// // let _inception = 42;
+/// // }
+/// let _inception = 42;
+/// ```
+///
+/// We should not lint ignored examples:
+/// ```rust,ignore
+/// fn main() {
+/// unimplemented!();
+/// }
+/// ```
+///
+/// Or even non-rust examples:
+/// ```text
+/// fn main() {
+/// is what starts the program
+/// }
+/// ```
+fn no_false_positives() {}
+
+/// Yields a parse error when interpreted as rust code:
+/// ```
+/// r#"hi"
+/// ```
+fn issue_6022() {}
+
+fn main() {
+ bad_doctests();
+ no_false_positives();
+}
diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr
new file mode 100644
index 000000000..05c7f9d33
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr
@@ -0,0 +1,28 @@
+error: needless `fn main` in doctest
+ --> $DIR/needless_doc_main.rs:7:4
+ |
+LL | /// fn main() {
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
+
+error: needless `fn main` in doctest
+ --> $DIR/needless_doc_main.rs:14:4
+ |
+LL | /// fn main() -> () {
+ | ^^^^^^^^^^^^^^^^^^
+
+error: needless `fn main` in doctest
+ --> $DIR/needless_doc_main.rs:21:4
+ |
+LL | /// fn main() {
+ | ^^^^^^^^^^^^
+
+error: needless `fn main` in doctest
+ --> $DIR/needless_doc_main.rs:28:4
+ |
+LL | /// fn main() {
+ | ^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed
new file mode 100644
index 000000000..c1685f7b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::needless_for_each)]
+#![allow(
+ unused,
+ clippy::needless_return,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
+
+use std::collections::HashMap;
+
+fn should_lint() {
+ let v: Vec<i32> = Vec::new();
+ let mut acc = 0;
+ for elem in v.iter() {
+ acc += elem;
+ }
+ for elem in v.into_iter() {
+ acc += elem;
+ }
+
+ for elem in [1, 2, 3].iter() {
+ acc += elem;
+ }
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ for (k, v) in hash_map.iter() {
+ acc += k + v;
+ }
+ for (k, v) in hash_map.iter_mut() {
+ acc += *k + *v;
+ }
+ for k in hash_map.keys() {
+ acc += k;
+ }
+ for v in hash_map.values() {
+ acc += v;
+ }
+
+ fn my_vec() -> Vec<i32> {
+ Vec::new()
+ }
+ for elem in my_vec().iter() {
+ acc += elem;
+ }
+}
+
+fn should_not_lint() {
+ let v: Vec<i32> = Vec::new();
+ let mut acc = 0;
+
+ // `for_each` argument is not closure.
+ fn print(x: &i32) {
+ println!("{}", x);
+ }
+ v.iter().for_each(print);
+
+ // User defined type.
+ struct MyStruct {
+ v: Vec<i32>,
+ }
+ impl MyStruct {
+ fn iter(&self) -> impl Iterator<Item = &i32> {
+ self.v.iter()
+ }
+ }
+ let s = MyStruct { v: Vec::new() };
+ s.iter().for_each(|elem| {
+ acc += elem;
+ });
+
+ // `for_each` follows long iterator chain.
+ v.iter().chain(v.iter()).for_each(|v| {
+ acc += v;
+ });
+ v.as_slice().iter().for_each(|v| {
+ acc += v;
+ });
+ s.v.iter().for_each(|v| {
+ acc += v;
+ });
+
+ // `return` is used in `Loop` of the closure.
+ v.iter().for_each(|v| {
+ for i in 0..*v {
+ if i == 10 {
+ return;
+ } else {
+ println!("{}", v);
+ }
+ }
+ if *v == 20 {
+ return;
+ } else {
+ println!("{}", v);
+ }
+ });
+
+ // Previously transformed iterator variable.
+ let it = v.iter();
+ it.chain(v.iter()).for_each(|elem| {
+ acc += elem;
+ });
+
+ // `for_each` is not directly in a statement.
+ match 1 {
+ _ => v.iter().for_each(|elem| {
+ acc += elem;
+ }),
+ }
+
+ // `for_each` is in a let bingind.
+ let _ = v.iter().for_each(|elem| {
+ acc += elem;
+ });
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs
new file mode 100644
index 000000000..ad17b0956
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs
@@ -0,0 +1,118 @@
+// run-rustfix
+#![warn(clippy::needless_for_each)]
+#![allow(
+ unused,
+ clippy::needless_return,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
+
+use std::collections::HashMap;
+
+fn should_lint() {
+ let v: Vec<i32> = Vec::new();
+ let mut acc = 0;
+ v.iter().for_each(|elem| {
+ acc += elem;
+ });
+ v.into_iter().for_each(|elem| {
+ acc += elem;
+ });
+
+ [1, 2, 3].iter().for_each(|elem| {
+ acc += elem;
+ });
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ hash_map.iter().for_each(|(k, v)| {
+ acc += k + v;
+ });
+ hash_map.iter_mut().for_each(|(k, v)| {
+ acc += *k + *v;
+ });
+ hash_map.keys().for_each(|k| {
+ acc += k;
+ });
+ hash_map.values().for_each(|v| {
+ acc += v;
+ });
+
+ fn my_vec() -> Vec<i32> {
+ Vec::new()
+ }
+ my_vec().iter().for_each(|elem| {
+ acc += elem;
+ });
+}
+
+fn should_not_lint() {
+ let v: Vec<i32> = Vec::new();
+ let mut acc = 0;
+
+ // `for_each` argument is not closure.
+ fn print(x: &i32) {
+ println!("{}", x);
+ }
+ v.iter().for_each(print);
+
+ // User defined type.
+ struct MyStruct {
+ v: Vec<i32>,
+ }
+ impl MyStruct {
+ fn iter(&self) -> impl Iterator<Item = &i32> {
+ self.v.iter()
+ }
+ }
+ let s = MyStruct { v: Vec::new() };
+ s.iter().for_each(|elem| {
+ acc += elem;
+ });
+
+ // `for_each` follows long iterator chain.
+ v.iter().chain(v.iter()).for_each(|v| {
+ acc += v;
+ });
+ v.as_slice().iter().for_each(|v| {
+ acc += v;
+ });
+ s.v.iter().for_each(|v| {
+ acc += v;
+ });
+
+ // `return` is used in `Loop` of the closure.
+ v.iter().for_each(|v| {
+ for i in 0..*v {
+ if i == 10 {
+ return;
+ } else {
+ println!("{}", v);
+ }
+ }
+ if *v == 20 {
+ return;
+ } else {
+ println!("{}", v);
+ }
+ });
+
+ // Previously transformed iterator variable.
+ let it = v.iter();
+ it.chain(v.iter()).for_each(|elem| {
+ acc += elem;
+ });
+
+ // `for_each` is not directly in a statement.
+ match 1 {
+ _ => v.iter().for_each(|elem| {
+ acc += elem;
+ }),
+ }
+
+ // `for_each` is in a let bingind.
+ let _ = v.iter().for_each(|elem| {
+ acc += elem;
+ });
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr
new file mode 100644
index 000000000..08e995851
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr
@@ -0,0 +1,123 @@
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:15:5
+ |
+LL | / v.iter().for_each(|elem| {
+LL | | acc += elem;
+LL | | });
+ | |_______^
+ |
+ = note: `-D clippy::needless-for-each` implied by `-D warnings`
+help: try
+ |
+LL ~ for elem in v.iter() {
+LL + acc += elem;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:18:5
+ |
+LL | / v.into_iter().for_each(|elem| {
+LL | | acc += elem;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for elem in v.into_iter() {
+LL + acc += elem;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:22:5
+ |
+LL | / [1, 2, 3].iter().for_each(|elem| {
+LL | | acc += elem;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for elem in [1, 2, 3].iter() {
+LL + acc += elem;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:27:5
+ |
+LL | / hash_map.iter().for_each(|(k, v)| {
+LL | | acc += k + v;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for (k, v) in hash_map.iter() {
+LL + acc += k + v;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:30:5
+ |
+LL | / hash_map.iter_mut().for_each(|(k, v)| {
+LL | | acc += *k + *v;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for (k, v) in hash_map.iter_mut() {
+LL + acc += *k + *v;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:33:5
+ |
+LL | / hash_map.keys().for_each(|k| {
+LL | | acc += k;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for k in hash_map.keys() {
+LL + acc += k;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:36:5
+ |
+LL | / hash_map.values().for_each(|v| {
+LL | | acc += v;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for v in hash_map.values() {
+LL + acc += v;
+LL + }
+ |
+
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_fixable.rs:43:5
+ |
+LL | / my_vec().iter().for_each(|elem| {
+LL | | acc += elem;
+LL | | });
+ | |_______^
+ |
+help: try
+ |
+LL ~ for elem in my_vec().iter() {
+LL + acc += elem;
+LL + }
+ |
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs
new file mode 100644
index 000000000..d765d7dab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs
@@ -0,0 +1,14 @@
+#![warn(clippy::needless_for_each)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+ let v: Vec<i32> = Vec::new();
+ // This is unfixable because the closure includes `return`.
+ v.iter().for_each(|v| {
+ if *v == 10 {
+ return;
+ } else {
+ println!("{}", v);
+ }
+ });
+}
diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
new file mode 100644
index 000000000..7893ff31a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
@@ -0,0 +1,30 @@
+error: needless use of `for_each`
+ --> $DIR/needless_for_each_unfixable.rs:7:5
+ |
+LL | / v.iter().for_each(|v| {
+LL | | if *v == 10 {
+LL | | return;
+LL | | } else {
+LL | | println!("{}", v);
+LL | | }
+LL | | });
+ | |_______^
+ |
+ = note: `-D clippy::needless-for-each` implied by `-D warnings`
+help: try
+ |
+LL ~ for v in v.iter() {
+LL + if *v == 10 {
+LL + return;
+LL + } else {
+LL + println!("{}", v);
+LL + }
+LL + }
+ |
+help: ...and replace `return` with `continue`
+ |
+LL | continue;
+ | ~~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed
new file mode 100644
index 000000000..fee8e3030
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init.fixed
@@ -0,0 +1,273 @@
+// run-rustfix
+#![feature(let_chains)]
+#![allow(
+ unused,
+ clippy::assign_op_pattern,
+ clippy::blocks_in_if_conditions,
+ clippy::let_and_return,
+ clippy::let_unit_value,
+ clippy::nonminimal_bool
+)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::rc::Rc;
+
+struct SignificantDrop;
+impl std::ops::Drop for SignificantDrop {
+ fn drop(&mut self) {
+ println!("dropped");
+ }
+}
+
+fn simple() {
+
+ let a = "zero";
+
+
+
+ let b = 1;
+ let c = 2;
+
+
+ let d: usize = 1;
+
+
+ let e = format!("{}", d);
+}
+
+fn main() {
+
+ let n = 1;
+ let a = match n {
+ 1 => "one",
+ _ => {
+ "two"
+ },
+ };
+
+
+ let b = if n == 3 {
+ "four"
+ } else {
+ "five"
+ };
+
+
+ let d = if true {
+ let temp = 5;
+ temp
+ } else {
+ 15
+ };
+
+
+ let e = if true {
+ format!("{} {}", a, b)
+ } else {
+ format!("{}", n)
+ };
+
+
+ let f = match 1 {
+ 1 => "three",
+ _ => return,
+ }; // has semi
+
+
+ let g: usize = if true {
+ 5
+ } else {
+ panic!();
+ };
+
+ // Drop order only matters if both are significant
+
+ let y = SignificantDrop;
+ let x = 1;
+
+
+ let y = 1;
+ let x = SignificantDrop;
+
+
+ // types that should be considered insignificant
+ let y = 1;
+ let y = "2";
+ let y = String::new();
+ let y = vec![3.0];
+ let y = HashMap::<usize, usize>::new();
+ let y = BTreeMap::<usize, usize>::new();
+ let y = HashSet::<usize>::new();
+ let y = BTreeSet::<usize>::new();
+ let y = Box::new(4);
+ let x = SignificantDrop;
+}
+
+async fn in_async() -> &'static str {
+ async fn f() -> &'static str {
+ "one"
+ }
+
+
+ let n = 1;
+ let a = match n {
+ 1 => f().await,
+ _ => {
+ "two"
+ },
+ };
+
+ a
+}
+
+const fn in_const() -> &'static str {
+ const fn f() -> &'static str {
+ "one"
+ }
+
+
+ let n = 1;
+ let a = match n {
+ 1 => f(),
+ _ => {
+ "two"
+ },
+ };
+
+ a
+}
+
+fn does_not_lint() {
+ let z;
+ if false {
+ z = 1;
+ }
+
+ let x;
+ let y;
+ if true {
+ x = 1;
+ } else {
+ y = 1;
+ }
+
+ let mut x;
+ if true {
+ x = 5;
+ x = 10 / x;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ let _ = match 1 {
+ 1 => x = 10,
+ _ => x = 20,
+ };
+
+ // using tuples would be possible, but not always preferable
+ let x;
+ let y;
+ if true {
+ x = 1;
+ y = 2;
+ } else {
+ x = 3;
+ y = 4;
+ }
+
+ // could match with a smarter heuristic to avoid multiple assignments
+ let x;
+ if true {
+ let mut y = 5;
+ y = 6;
+ x = y;
+ } else {
+ x = 2;
+ }
+
+ let (x, y);
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ y = 3;
+
+ macro_rules! assign {
+ ($i:ident) => {
+ $i = 1;
+ };
+ }
+ let x;
+ assign!(x);
+
+ let x;
+ if true {
+ assign!(x);
+ } else {
+ x = 2;
+ }
+
+ macro_rules! in_macro {
+ () => {
+ let x;
+ x = 1;
+
+ let x;
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ };
+ }
+ in_macro!();
+
+ // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
+ let x;
+ if let Some(n) = Some("v") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ if true && let Some(n) = Some("let chains too") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ // ignore mut bindings
+ // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
+ // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
+ let mut x: usize;
+ x = 1;
+ x = 2;
+ x = 3;
+
+ // should not move the declaration if `x` has a significant drop, and there
+ // is another binding with a significant drop between it and the first usage
+ let x;
+ let y = SignificantDrop;
+ x = SignificantDrop;
+}
+
+#[rustfmt::skip]
+fn issue8911() -> u32 {
+ let x;
+ match 1 {
+ _ if { x = 1; false } => return 1,
+ _ => return 2,
+ }
+
+ let x;
+ if { x = 1; true } {
+ return 1;
+ } else {
+ return 2;
+ }
+
+ 3
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
new file mode 100644
index 000000000..402d9f9ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -0,0 +1,273 @@
+// run-rustfix
+#![feature(let_chains)]
+#![allow(
+ unused,
+ clippy::assign_op_pattern,
+ clippy::blocks_in_if_conditions,
+ clippy::let_and_return,
+ clippy::let_unit_value,
+ clippy::nonminimal_bool
+)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::rc::Rc;
+
+struct SignificantDrop;
+impl std::ops::Drop for SignificantDrop {
+ fn drop(&mut self) {
+ println!("dropped");
+ }
+}
+
+fn simple() {
+ let a;
+ a = "zero";
+
+ let b;
+ let c;
+ b = 1;
+ c = 2;
+
+ let d: usize;
+ d = 1;
+
+ let e;
+ e = format!("{}", d);
+}
+
+fn main() {
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = "one",
+ _ => {
+ a = "two";
+ },
+ }
+
+ let b;
+ if n == 3 {
+ b = "four";
+ } else {
+ b = "five"
+ }
+
+ let d;
+ if true {
+ let temp = 5;
+ d = temp;
+ } else {
+ d = 15;
+ }
+
+ let e;
+ if true {
+ e = format!("{} {}", a, b);
+ } else {
+ e = format!("{}", n);
+ }
+
+ let f;
+ match 1 {
+ 1 => f = "three",
+ _ => return,
+ }; // has semi
+
+ let g: usize;
+ if true {
+ g = 5;
+ } else {
+ panic!();
+ }
+
+ // Drop order only matters if both are significant
+ let x;
+ let y = SignificantDrop;
+ x = 1;
+
+ let x;
+ let y = 1;
+ x = SignificantDrop;
+
+ let x;
+ // types that should be considered insignificant
+ let y = 1;
+ let y = "2";
+ let y = String::new();
+ let y = vec![3.0];
+ let y = HashMap::<usize, usize>::new();
+ let y = BTreeMap::<usize, usize>::new();
+ let y = HashSet::<usize>::new();
+ let y = BTreeSet::<usize>::new();
+ let y = Box::new(4);
+ x = SignificantDrop;
+}
+
+async fn in_async() -> &'static str {
+ async fn f() -> &'static str {
+ "one"
+ }
+
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = f().await,
+ _ => {
+ a = "two";
+ },
+ }
+
+ a
+}
+
+const fn in_const() -> &'static str {
+ const fn f() -> &'static str {
+ "one"
+ }
+
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = f(),
+ _ => {
+ a = "two";
+ },
+ }
+
+ a
+}
+
+fn does_not_lint() {
+ let z;
+ if false {
+ z = 1;
+ }
+
+ let x;
+ let y;
+ if true {
+ x = 1;
+ } else {
+ y = 1;
+ }
+
+ let mut x;
+ if true {
+ x = 5;
+ x = 10 / x;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ let _ = match 1 {
+ 1 => x = 10,
+ _ => x = 20,
+ };
+
+ // using tuples would be possible, but not always preferable
+ let x;
+ let y;
+ if true {
+ x = 1;
+ y = 2;
+ } else {
+ x = 3;
+ y = 4;
+ }
+
+ // could match with a smarter heuristic to avoid multiple assignments
+ let x;
+ if true {
+ let mut y = 5;
+ y = 6;
+ x = y;
+ } else {
+ x = 2;
+ }
+
+ let (x, y);
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ y = 3;
+
+ macro_rules! assign {
+ ($i:ident) => {
+ $i = 1;
+ };
+ }
+ let x;
+ assign!(x);
+
+ let x;
+ if true {
+ assign!(x);
+ } else {
+ x = 2;
+ }
+
+ macro_rules! in_macro {
+ () => {
+ let x;
+ x = 1;
+
+ let x;
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ };
+ }
+ in_macro!();
+
+ // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
+ let x;
+ if let Some(n) = Some("v") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ if true && let Some(n) = Some("let chains too") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ // ignore mut bindings
+ // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
+ // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
+ let mut x: usize;
+ x = 1;
+ x = 2;
+ x = 3;
+
+ // should not move the declaration if `x` has a significant drop, and there
+ // is another binding with a significant drop between it and the first usage
+ let x;
+ let y = SignificantDrop;
+ x = SignificantDrop;
+}
+
+#[rustfmt::skip]
+fn issue8911() -> u32 {
+ let x;
+ match 1 {
+ _ if { x = 1; false } => return 1,
+ _ => return 2,
+ }
+
+ let x;
+ if { x = 1; true } {
+ return 1;
+ } else {
+ return 2;
+ }
+
+ 3
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
new file mode 100644
index 000000000..313cdbbeb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init.stderr
@@ -0,0 +1,274 @@
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:23:5
+ |
+LL | let a;
+ | ^^^^^^ created here
+LL | a = "zero";
+ | ^^^^^^^^^^ initialised here
+ |
+ = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+ |
+LL | let a = "zero";
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:26:5
+ |
+LL | let b;
+ | ^^^^^^ created here
+LL | let c;
+LL | b = 1;
+ | ^^^^^ initialised here
+ |
+help: declare `b` here
+ |
+LL | let b = 1;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:27:5
+ |
+LL | let c;
+ | ^^^^^^ created here
+LL | b = 1;
+LL | c = 2;
+ | ^^^^^ initialised here
+ |
+help: declare `c` here
+ |
+LL | let c = 2;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:31:5
+ |
+LL | let d: usize;
+ | ^^^^^^^^^^^^^ created here
+LL | d = 1;
+ | ^^^^^ initialised here
+ |
+help: declare `d` here
+ |
+LL | let d: usize = 1;
+ | ~~~~~~~~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:34:5
+ |
+LL | let e;
+ | ^^^^^^ created here
+LL | e = format!("{}", d);
+ | ^^^^^^^^^^^^^^^^^^^^ initialised here
+ |
+help: declare `e` here
+ |
+LL | let e = format!("{}", d);
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:39:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => "one",
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:48:5
+ |
+LL | let b;
+ | ^^^^^^
+ |
+help: declare `b` here
+ |
+LL | let b = if n == 3 {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ "four"
+LL | } else {
+LL ~ "five"
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:55:5
+ |
+LL | let d;
+ | ^^^^^^
+ |
+help: declare `d` here
+ |
+LL | let d = if true {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ temp
+LL | } else {
+LL ~ 15
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:63:5
+ |
+LL | let e;
+ | ^^^^^^
+ |
+help: declare `e` here
+ |
+LL | let e = if true {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ format!("{} {}", a, b)
+LL | } else {
+LL ~ format!("{}", n)
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:70:5
+ |
+LL | let f;
+ | ^^^^^^
+ |
+help: declare `f` here
+ |
+LL | let f = match 1 {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL - 1 => f = "three",
+LL + 1 => "three",
+ |
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:76:5
+ |
+LL | let g: usize;
+ | ^^^^^^^^^^^^^
+ |
+help: declare `g` here
+ |
+LL | let g: usize = if true {
+ | ++++++++++++++
+help: remove the assignments from the branches
+ |
+LL - g = 5;
+LL + 5
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:84:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+LL | let y = SignificantDrop;
+LL | x = 1;
+ | ^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = 1;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:88:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+LL | let y = 1;
+LL | x = SignificantDrop;
+ | ^^^^^^^^^^^^^^^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = SignificantDrop;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:92:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+...
+LL | x = SignificantDrop;
+ | ^^^^^^^^^^^^^^^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = SignificantDrop;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:111:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => f().await,
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:128:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => f(),
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
new file mode 100644
index 000000000..fc686b1da
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -0,0 +1,422 @@
+#![warn(clippy::needless_lifetimes)]
+#![allow(
+ dead_code,
+ clippy::boxed_local,
+ clippy::needless_pass_by_value,
+ clippy::unnecessary_wraps,
+ dyn_drop,
+ clippy::get_first
+)]
+
+fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
+
+fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
+
+// No error; same lifetime on two params.
+fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
+
+// No error; static involved.
+fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
+
+fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
+
+fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
+ x
+}
+
+// No error; multiple input refs.
+fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
+ x
+}
+
+// No error; multiple input refs.
+fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
+ x
+}
+
+// No error; multiple input refs
+async fn func<'a>(args: &[&'a str]) -> Option<&'a str> {
+ args.get(0).cloned()
+}
+
+// No error; static involved.
+fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
+ x
+}
+
+// No error.
+fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
+ Ok(x)
+}
+
+// No error; two input refs.
+fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
+ x.unwrap()
+}
+
+fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
+ Ok(x)
+}
+
+// Where-clause, but without lifetimes.
+fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
+where
+ T: Copy,
+{
+ Ok(x)
+}
+
+type Ref<'r> = &'r u8;
+
+// No error; same lifetime on two params.
+fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
+
+fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
+where
+ 'b: 'a,
+{
+}
+
+struct Lt<'a, I: 'static> {
+ x: &'a I,
+}
+
+// No error; fn bound references `'a`.
+fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+where
+ F: Fn(Lt<'a, I>) -> Lt<'a, I>,
+{
+ unreachable!()
+}
+
+fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+where
+ for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
+{
+ unreachable!()
+}
+
+// No error; see below.
+fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
+ f(x);
+}
+
+fn fn_bound_3_cannot_elide() {
+ let x = 42;
+ let p = &x;
+ let mut q = &x;
+ // This will fail if we elide lifetimes of `fn_bound_3`.
+ fn_bound_3(p, |y| q = y);
+}
+
+// No error; multiple input refs.
+fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
+ if cond { x } else { f() }
+}
+
+struct X {
+ x: u8,
+}
+
+impl X {
+ fn self_and_out<'s>(&'s self) -> &'s u8 {
+ &self.x
+ }
+
+ // No error; multiple input refs.
+ fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
+ &self.x
+ }
+
+ fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
+
+ // No error; same lifetimes on two params.
+ fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
+}
+
+struct Foo<'a>(&'a u8);
+
+impl<'a> Foo<'a> {
+ // No error; lifetime `'a` not defined in method.
+ fn self_shared_lifetime(&self, _: &'a u8) {}
+ // No error; bounds exist.
+ fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
+}
+
+fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
+ unimplemented!()
+}
+
+fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
+fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
+fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes.
+fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
+ unimplemented!()
+}
+
+trait WithLifetime<'a> {}
+
+type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
+
+// Should not warn because it won't build without the lifetime.
+fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
+ unimplemented!()
+}
+
+// Should warn because there is no lifetime on `Drop`, so this would be
+// unambiguous if we elided the lifetime.
+fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
+ unimplemented!()
+}
+
+type FooAlias<'a> = Foo<'a>;
+
+fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
+fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
+fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
+ unimplemented!()
+}
+
+// No warning; two input lifetimes.
+fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
+ unimplemented!()
+}
+
+fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
+ unimplemented!()
+}
+
+fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
+ unimplemented!()
+}
+
+fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
+ unimplemented!()
+}
+fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
+ unimplemented!()
+}
+
+// Don't warn on these; see issue #292.
+fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
+ unimplemented!()
+}
+
+// See issue #740.
+struct Test {
+ vec: Vec<usize>,
+}
+
+impl Test {
+ fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
+ unimplemented!()
+ }
+}
+
+trait LintContext<'a> {}
+
+fn f<'a, T: LintContext<'a>>(_: &T) {}
+
+fn test<'a>(x: &'a [u8]) -> u8 {
+ let y: &'a u8 = &x[5];
+ *y
+}
+
+// Issue #3284: give hint regarding lifetime in return type.
+struct Cow<'a> {
+ x: &'a str,
+}
+fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
+ unimplemented!()
+}
+
+// Make sure we still warn on implementations
+mod issue4291 {
+ trait BadTrait {
+ fn needless_lt<'a>(x: &'a u8) {}
+ }
+
+ impl BadTrait for () {
+ fn needless_lt<'a>(_x: &'a u8) {}
+ }
+}
+
+mod issue2944 {
+ trait Foo {}
+ struct Bar;
+ struct Baz<'a> {
+ bar: &'a Bar,
+ }
+
+ impl<'a> Foo for Baz<'a> {}
+ impl Bar {
+ fn baz<'a>(&'a self) -> impl Foo + 'a {
+ Baz { bar: self }
+ }
+ }
+}
+
+mod nested_elision_sites {
+ // issue #issue2944
+
+ // closure trait bounds subject to nested elision
+ // don't lint because they refer to outer lifetimes
+ fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+ move || i
+ }
+ fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
+ move || i
+ }
+ fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
+ move || i
+ }
+
+ // don't lint
+ fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
+ f()
+ }
+ fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+ move || i
+ }
+ // lint
+ fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
+ f(i)
+ }
+ fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
+ f()
+ }
+ // lint
+ fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
+ where
+ T: Fn() -> &'a i32,
+ {
+ f()
+ }
+ // lint
+ fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
+ where
+ T: Fn(&i32) -> &i32,
+ {
+ f(i)
+ }
+
+ // don't lint
+ fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
+ f(i)
+ }
+ fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
+ |i| i
+ }
+ // lint
+ fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
+ f(i)
+ }
+
+ // don't lint
+ fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
+ |f| 42
+ }
+ fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
+ |f| ()
+ }
+
+ // lint
+ fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
+ |f| 42
+ }
+ fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
+ |f| ()
+ }
+}
+
+mod issue6159 {
+ use std::ops::Deref;
+ pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
+ where
+ T: Deref,
+ F: FnOnce(&'a T::Target) -> R,
+ {
+ f(x.deref())
+ }
+}
+
+mod issue7296 {
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ struct Foo;
+ impl Foo {
+ fn implicit<'a>(&'a self) -> &'a () {
+ &()
+ }
+ fn implicit_mut<'a>(&'a mut self) -> &'a () {
+ &()
+ }
+
+ fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
+ &()
+ }
+ fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
+ &()
+ }
+
+ fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+ &()
+ }
+ }
+
+ trait Bar {
+ fn implicit<'a>(&'a self) -> &'a ();
+ fn implicit_provided<'a>(&'a self) -> &'a () {
+ &()
+ }
+
+ fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
+ fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
+ &()
+ }
+
+ fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
+ fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+ &()
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
new file mode 100644
index 000000000..3c428fd46
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
@@ -0,0 +1,190 @@
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:11:1
+ |
+LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:13:1
+ |
+LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:23:1
+ |
+LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:57:1
+ |
+LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:62:1
+ |
+LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:74:1
+ |
+LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:98:1
+ |
+LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:128:5
+ |
+LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:137:5
+ |
+LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:156:1
+ |
+LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:186:1
+ |
+LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:192:1
+ |
+LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:211:1
+ |
+LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:219:1
+ |
+LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:255:1
+ |
+LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:262:9
+ |
+LL | fn needless_lt<'a>(x: &'a u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:266:9
+ |
+LL | fn needless_lt<'a>(_x: &'a u8) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:279:9
+ |
+LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:311:5
+ |
+LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:320:5
+ |
+LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:332:5
+ |
+LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:347:5
+ |
+LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:360:5
+ |
+LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:363:5
+ |
+LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:385:9
+ |
+LL | fn implicit<'a>(&'a self) -> &'a () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:388:9
+ |
+LL | fn implicit_mut<'a>(&'a mut self) -> &'a () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:399:9
+ |
+LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:405:9
+ |
+LL | fn implicit<'a>(&'a self) -> &'a ();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:406:9
+ |
+LL | fn implicit_provided<'a>(&'a self) -> &'a () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:415:9
+ |
+LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
+ --> $DIR/needless_lifetimes.rs:416:9
+ |
+LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 31 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed
new file mode 100644
index 000000000..0c9178fb8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_match.fixed
@@ -0,0 +1,210 @@
+// run-rustfix
+#![warn(clippy::needless_match)]
+#![allow(clippy::manual_map)]
+#![allow(dead_code)]
+
+#[derive(Clone, Copy)]
+enum Simple {
+ A,
+ B,
+ C,
+ D,
+}
+
+fn useless_match() {
+ let i = 10;
+ let _: i32 = i;
+ let s = "test";
+ let _: &str = s;
+}
+
+fn custom_type_match() {
+ let se = Simple::A;
+ let _: Simple = se;
+ // Don't trigger
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ _ => Simple::C,
+ };
+ // Mingled, don't trigger
+ let _: Simple = match se {
+ Simple::A => Simple::B,
+ Simple::B => Simple::C,
+ Simple::C => Simple::D,
+ Simple::D => Simple::A,
+ };
+}
+
+fn option_match(x: Option<i32>) {
+ let _: Option<i32> = x;
+ // Don't trigger, this is the case for manual_map_option
+ let _: Option<i32> = match x {
+ Some(a) => Some(-a),
+ None => None,
+ };
+}
+
+fn func_ret_err<T>(err: T) -> Result<i32, T> {
+ Err(err)
+}
+
+fn result_match() {
+ let _: Result<i32, i32> = Ok(1);
+ let _: Result<i32, i32> = func_ret_err(0_i32);
+ // as ref, don't trigger
+ let res = &func_ret_err(0_i32);
+ let _: Result<&i32, &i32> = match *res {
+ Ok(ref x) => Ok(x),
+ Err(ref x) => Err(x),
+ };
+}
+
+fn if_let_option() {
+ let _ = Some(1);
+
+ fn do_something() {}
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ Some(a)
+ } else {
+ do_something();
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ do_something();
+ Some(a)
+ } else {
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+ fn f(x: i32) -> Result<Option<i32>, ()> {
+ Ok(Some(x))
+ }
+ // Don't trigger
+ let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+ Ok(())
+}
+
+fn if_let_result() {
+ let x: Result<i32, i32> = Ok(1);
+ let _: Result<i32, i32> = x;
+ let _: Result<i32, i32> = x;
+ // Input type mismatch, don't trigger
+ #[allow(clippy::question_mark)]
+ let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+}
+
+fn if_let_custom_enum(x: Simple) {
+ let _: Simple = x;
+
+ // Don't trigger
+ let _: Simple = if let Simple::A = x {
+ Simple::A
+ } else if true {
+ Simple::B
+ } else {
+ x
+ };
+}
+
+mod issue8542 {
+ #[derive(Clone, Copy)]
+ enum E {
+ VariantA(u8, u8),
+ VariantB(u8, bool),
+ }
+
+ enum Complex {
+ A(u8),
+ B(u8, bool),
+ C(u8, i32, f64),
+ D(E, bool),
+ }
+
+ fn match_test() {
+ let ce = Complex::B(8, false);
+ let aa = 0_u8;
+ let bb = false;
+
+ let _: Complex = ce;
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(_) => Complex::A(aa),
+ Complex::B(_, b) => Complex::B(aa, b),
+ Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+ Complex::D(e, b) => Complex::D(e, b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, _) => Complex::B(a, bb),
+ Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+ _ => ce,
+ };
+ }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+ trait Trait {}
+ struct Struct;
+ impl Trait for Struct {}
+
+ fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+ match s {
+ Some(s) => Some(s),
+ None => None,
+ }
+ }
+
+ fn lint_tests() {
+ let option: Option<&Struct> = None;
+ let _: Option<&dyn Trait> = match option {
+ Some(s) => Some(s),
+ None => None,
+ };
+
+ let _: Option<&dyn Trait> = if true {
+ match option {
+ Some(s) => Some(s),
+ None => None,
+ }
+ } else {
+ None
+ };
+
+ let result: Result<&Struct, i32> = Err(0);
+ let _: Result<&dyn Trait, i32> = match result {
+ Ok(s) => Ok(s),
+ Err(e) => Err(e),
+ };
+
+ let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+ }
+}
+
+trait Tr {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result<i32, i32> {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+ match self {
+ Ok(x) => Ok(x),
+ Err(e) => Err(e),
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs
new file mode 100644
index 000000000..f66f01d7c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_match.rs
@@ -0,0 +1,247 @@
+// run-rustfix
+#![warn(clippy::needless_match)]
+#![allow(clippy::manual_map)]
+#![allow(dead_code)]
+
+#[derive(Clone, Copy)]
+enum Simple {
+ A,
+ B,
+ C,
+ D,
+}
+
+fn useless_match() {
+ let i = 10;
+ let _: i32 = match i {
+ 0 => 0,
+ 1 => 1,
+ 2 => 2,
+ _ => i,
+ };
+ let s = "test";
+ let _: &str = match s {
+ "a" => "a",
+ "b" => "b",
+ s => s,
+ };
+}
+
+fn custom_type_match() {
+ let se = Simple::A;
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ Simple::C => Simple::C,
+ Simple::D => Simple::D,
+ };
+ // Don't trigger
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ _ => Simple::C,
+ };
+ // Mingled, don't trigger
+ let _: Simple = match se {
+ Simple::A => Simple::B,
+ Simple::B => Simple::C,
+ Simple::C => Simple::D,
+ Simple::D => Simple::A,
+ };
+}
+
+fn option_match(x: Option<i32>) {
+ let _: Option<i32> = match x {
+ Some(a) => Some(a),
+ None => None,
+ };
+ // Don't trigger, this is the case for manual_map_option
+ let _: Option<i32> = match x {
+ Some(a) => Some(-a),
+ None => None,
+ };
+}
+
+fn func_ret_err<T>(err: T) -> Result<i32, T> {
+ Err(err)
+}
+
+fn result_match() {
+ let _: Result<i32, i32> = match Ok(1) {
+ Ok(a) => Ok(a),
+ Err(err) => Err(err),
+ };
+ let _: Result<i32, i32> = match func_ret_err(0_i32) {
+ Err(err) => Err(err),
+ Ok(a) => Ok(a),
+ };
+ // as ref, don't trigger
+ let res = &func_ret_err(0_i32);
+ let _: Result<&i32, &i32> = match *res {
+ Ok(ref x) => Ok(x),
+ Err(ref x) => Err(x),
+ };
+}
+
+fn if_let_option() {
+ let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+
+ fn do_something() {}
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ Some(a)
+ } else {
+ do_something();
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ do_something();
+ Some(a)
+ } else {
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+ fn f(x: i32) -> Result<Option<i32>, ()> {
+ Ok(Some(x))
+ }
+ // Don't trigger
+ let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+ Ok(())
+}
+
+fn if_let_result() {
+ let x: Result<i32, i32> = Ok(1);
+ let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
+ let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
+ // Input type mismatch, don't trigger
+ #[allow(clippy::question_mark)]
+ let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+}
+
+fn if_let_custom_enum(x: Simple) {
+ let _: Simple = if let Simple::A = x {
+ Simple::A
+ } else if let Simple::B = x {
+ Simple::B
+ } else if let Simple::C = x {
+ Simple::C
+ } else {
+ x
+ };
+
+ // Don't trigger
+ let _: Simple = if let Simple::A = x {
+ Simple::A
+ } else if true {
+ Simple::B
+ } else {
+ x
+ };
+}
+
+mod issue8542 {
+ #[derive(Clone, Copy)]
+ enum E {
+ VariantA(u8, u8),
+ VariantB(u8, bool),
+ }
+
+ enum Complex {
+ A(u8),
+ B(u8, bool),
+ C(u8, i32, f64),
+ D(E, bool),
+ }
+
+ fn match_test() {
+ let ce = Complex::B(8, false);
+ let aa = 0_u8;
+ let bb = false;
+
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, b) => Complex::B(a, b),
+ Complex::C(a, b, c) => Complex::C(a, b, c),
+ Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+ Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(_) => Complex::A(aa),
+ Complex::B(_, b) => Complex::B(aa, b),
+ Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+ Complex::D(e, b) => Complex::D(e, b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, _) => Complex::B(a, bb),
+ Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+ _ => ce,
+ };
+ }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+ trait Trait {}
+ struct Struct;
+ impl Trait for Struct {}
+
+ fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+ match s {
+ Some(s) => Some(s),
+ None => None,
+ }
+ }
+
+ fn lint_tests() {
+ let option: Option<&Struct> = None;
+ let _: Option<&dyn Trait> = match option {
+ Some(s) => Some(s),
+ None => None,
+ };
+
+ let _: Option<&dyn Trait> = if true {
+ match option {
+ Some(s) => Some(s),
+ None => None,
+ }
+ } else {
+ None
+ };
+
+ let result: Result<&Struct, i32> = Err(0);
+ let _: Result<&dyn Trait, i32> = match result {
+ Ok(s) => Ok(s),
+ Err(e) => Err(e),
+ };
+
+ let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+ }
+}
+
+trait Tr {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result<i32, i32> {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+ match self {
+ Ok(x) => Ok(x),
+ Err(e) => Err(e),
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr
new file mode 100644
index 000000000..5bc79800a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_match.stderr
@@ -0,0 +1,113 @@
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:16:18
+ |
+LL | let _: i32 = match i {
+ | __________________^
+LL | | 0 => 0,
+LL | | 1 => 1,
+LL | | 2 => 2,
+LL | | _ => i,
+LL | | };
+ | |_____^ help: replace it with: `i`
+ |
+ = note: `-D clippy::needless-match` implied by `-D warnings`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:23:19
+ |
+LL | let _: &str = match s {
+ | ___________________^
+LL | | "a" => "a",
+LL | | "b" => "b",
+LL | | s => s,
+LL | | };
+ | |_____^ help: replace it with: `s`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:32:21
+ |
+LL | let _: Simple = match se {
+ | _____________________^
+LL | | Simple::A => Simple::A,
+LL | | Simple::B => Simple::B,
+LL | | Simple::C => Simple::C,
+LL | | Simple::D => Simple::D,
+LL | | };
+ | |_____^ help: replace it with: `se`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:54:26
+ |
+LL | let _: Option<i32> = match x {
+ | __________________________^
+LL | | Some(a) => Some(a),
+LL | | None => None,
+LL | | };
+ | |_____^ help: replace it with: `x`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:70:31
+ |
+LL | let _: Result<i32, i32> = match Ok(1) {
+ | _______________________________^
+LL | | Ok(a) => Ok(a),
+LL | | Err(err) => Err(err),
+LL | | };
+ | |_____^ help: replace it with: `Ok(1)`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:74:31
+ |
+LL | let _: Result<i32, i32> = match func_ret_err(0_i32) {
+ | _______________________________^
+LL | | Err(err) => Err(err),
+LL | | Ok(a) => Ok(a),
+LL | | };
+ | |_____^ help: replace it with: `func_ret_err(0_i32)`
+
+error: this if-let expression is unnecessary
+ --> $DIR/needless_match.rs:87:13
+ |
+LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
+
+error: this if-let expression is unnecessary
+ --> $DIR/needless_match.rs:122:31
+ |
+LL | let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+
+error: this if-let expression is unnecessary
+ --> $DIR/needless_match.rs:123:31
+ |
+LL | let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+
+error: this if-let expression is unnecessary
+ --> $DIR/needless_match.rs:130:21
+ |
+LL | let _: Simple = if let Simple::A = x {
+ | _____________________^
+LL | | Simple::A
+LL | | } else if let Simple::B = x {
+LL | | Simple::B
+... |
+LL | | x
+LL | | };
+ | |_____^ help: replace it with: `x`
+
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:169:26
+ |
+LL | let _: Complex = match ce {
+ | __________________________^
+LL | | Complex::A(a) => Complex::A(a),
+LL | | Complex::B(a, b) => Complex::B(a, b),
+LL | | Complex::C(a, b, c) => Complex::C(a, b, c),
+LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+LL | | };
+ | |_________^ help: replace it with: `ce`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
new file mode 100644
index 000000000..acd22c6bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
@@ -0,0 +1,55 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
+
+fn main() {
+ // should lint
+ let _: Option<&usize> = Some(&1);
+ let _: Option<&mut usize> = Some(&mut 1);
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let _ = x;
+
+ // should not lint
+ let _ = Some(Box::new(1)).as_deref();
+ let _ = Some(Box::new(1)).as_deref_mut();
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
+ // #7846
+ let mut i = 0;
+ let mut opt_vec = vec![Some(&mut i)];
+ opt_vec[0].as_deref_mut().unwrap();
+
+ let mut i = 0;
+ let x = &mut Some(&mut i);
+ (*x).as_deref_mut();
+
+ // #8047
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ x.as_deref_mut();
+ dbg!(x);
+}
+
+struct S<'a> {
+ opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+ s.opt.as_deref_mut()
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
new file mode 100644
index 000000000..61eda5052
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
+
+fn main() {
+ // should lint
+ let _: Option<&usize> = Some(&1).as_deref();
+ let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let _ = x.as_deref_mut();
+
+ // should not lint
+ let _ = Some(Box::new(1)).as_deref();
+ let _ = Some(Box::new(1)).as_deref_mut();
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
+ // #7846
+ let mut i = 0;
+ let mut opt_vec = vec![Some(&mut i)];
+ opt_vec[0].as_deref_mut().unwrap();
+
+ let mut i = 0;
+ let x = &mut Some(&mut i);
+ (*x).as_deref_mut();
+
+ // #8047
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ x.as_deref_mut();
+ dbg!(x);
+}
+
+struct S<'a> {
+ opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+ s.opt.as_deref_mut()
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
new file mode 100644
index 000000000..bc07db5b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
@@ -0,0 +1,22 @@
+error: derefed type is same as origin
+ --> $DIR/needless_option_as_deref.rs:8:29
+ |
+LL | let _: Option<&usize> = Some(&1).as_deref();
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
+ |
+ = note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
+
+error: derefed type is same as origin
+ --> $DIR/needless_option_as_deref.rs:9:33
+ |
+LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
+
+error: derefed type is same as origin
+ --> $DIR/needless_option_as_deref.rs:13:13
+ |
+LL | let _ = x.as_deref_mut();
+ | ^^^^^^^^^^^^^^^^ help: try this: `x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_option_take.fixed b/src/tools/clippy/tests/ui/needless_option_take.fixed
new file mode 100644
index 000000000..29691e816
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_take.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_take.rs b/src/tools/clippy/tests/ui/needless_option_take.rs
new file mode 100644
index 000000000..9f4109eb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_take.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref().take();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr
new file mode 100644
index 000000000..cb3bf015b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_take.stderr
@@ -0,0 +1,10 @@
+error: called `Option::take()` on a temporary value
+ --> $DIR/needless_option_take.rs:14:5
+ |
+LL | x.as_ref().take();
+ | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
+ |
+ = note: `-D clippy::needless-option-take` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_parens_on_range_literals.fixed b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.fixed
new file mode 100644
index 000000000..1bd75c806
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+// edition:2018
+
+#![warn(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::almost_complete_letter_range)]
+
+fn main() {
+ let _ = 'a'..='z';
+ let _ = 'a'..'z';
+ let _ = (1.)..2.;
+ let _ = (1.)..2.;
+ let _ = 'a'..;
+ let _ = ..'z';
+}
diff --git a/src/tools/clippy/tests/ui/needless_parens_on_range_literals.rs b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.rs
new file mode 100644
index 000000000..7abb8a1ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+// edition:2018
+
+#![warn(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::almost_complete_letter_range)]
+
+fn main() {
+ let _ = ('a')..=('z');
+ let _ = 'a'..('z');
+ let _ = (1.)..2.;
+ let _ = (1.)..(2.);
+ let _ = ('a')..;
+ let _ = ..('z');
+}
diff --git a/src/tools/clippy/tests/ui/needless_parens_on_range_literals.stderr b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.stderr
new file mode 100644
index 000000000..505f7ac91
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_parens_on_range_literals.stderr
@@ -0,0 +1,40 @@
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:8:13
+ |
+LL | let _ = ('a')..=('z');
+ | ^^^^^ help: try: `'a'`
+ |
+ = note: `-D clippy::needless-parens-on-range-literals` implied by `-D warnings`
+
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:8:21
+ |
+LL | let _ = ('a')..=('z');
+ | ^^^^^ help: try: `'z'`
+
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:9:18
+ |
+LL | let _ = 'a'..('z');
+ | ^^^^^ help: try: `'z'`
+
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:11:19
+ |
+LL | let _ = (1.)..(2.);
+ | ^^^^ help: try: `2.`
+
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:12:13
+ |
+LL | let _ = ('a')..;
+ | ^^^^^ help: try: `'a'`
+
+error: needless parenthesis on range literals can be removed
+ --> $DIR/needless_parens_on_range_literals.rs:13:15
+ |
+LL | let _ = ..('z');
+ | ^^^^^ help: try: `'z'`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs
new file mode 100644
index 000000000..5a35b100a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs
@@ -0,0 +1,160 @@
+#![warn(clippy::needless_pass_by_value)]
+#![allow(
+ dead_code,
+ clippy::single_match,
+ clippy::redundant_pattern_matching,
+ clippy::option_option,
+ clippy::redundant_clone
+)]
+
+use std::borrow::Borrow;
+use std::collections::HashSet;
+use std::convert::AsRef;
+use std::mem::MaybeUninit;
+
+// `v` should be warned
+// `w`, `x` and `y` are allowed (moved or mutated)
+fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
+ assert_eq!(v.len(), 42);
+
+ consume(w);
+
+ x.push(T::default());
+
+ y
+}
+
+fn consume<T>(_: T) {}
+
+struct Wrapper(String);
+
+fn bar(x: String, y: Wrapper) {
+ assert_eq!(x.len(), 42);
+ assert_eq!(y.0.len(), 42);
+}
+
+// V implements `Borrow<V>`, but should be warned correctly
+fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
+ println!("{}", t.borrow());
+ println!("{}", u.as_ref());
+ consume(&v);
+}
+
+// ok
+fn test_fn<F: Fn(i32) -> i32>(f: F) {
+ f(1);
+}
+
+// x should be warned, but y is ok
+fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
+ match x {
+ Some(Some(_)) => 1, // not moved
+ _ => 0,
+ };
+
+ match y {
+ Some(Some(s)) => consume(s), // moved
+ _ => (),
+ };
+}
+
+// x and y should be warned, but z is ok
+fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ let Wrapper(s) = z; // moved
+ let Wrapper(ref t) = y; // not moved
+ let Wrapper(_) = y; // still not moved
+
+ assert_eq!(x.0.len(), s.len());
+ println!("{}", t);
+}
+
+trait Foo {}
+
+// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead
+trait Serialize {}
+impl<'a, T> Serialize for &'a T where T: Serialize {}
+impl Serialize for i32 {}
+
+fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+
+fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ s.capacity();
+ let _ = t.clone();
+ u.capacity();
+ let _ = v.clone();
+}
+
+struct S<T, U>(T, U);
+
+impl<T: Serialize, U> S<T, U> {
+ fn foo(
+ self,
+ // taking `self` by value is always allowed
+ s: String,
+ t: String,
+ ) -> usize {
+ s.len() + t.capacity()
+ }
+
+ fn bar(_t: T, // Ok, since `&T: Serialize` too
+ ) {
+ }
+
+ fn baz(&self, _u: U, _s: Self) {}
+}
+
+trait FalsePositive {
+ fn visit_str(s: &str);
+ fn visit_string(s: String) {
+ Self::visit_str(&s);
+ }
+}
+
+// shouldn't warn on extern funcs
+extern "C" fn ext(x: MaybeUninit<usize>) -> usize {
+ unsafe { x.assume_init() }
+}
+
+// exempt RangeArgument
+fn range<T: ::std::ops::RangeBounds<usize>>(range: T) {
+ let _ = range.start_bound();
+}
+
+struct CopyWrapper(u32);
+
+fn bar_copy(x: u32, y: CopyWrapper) {
+ assert_eq!(x, 42);
+ assert_eq!(y.0, 42);
+}
+
+// x and y should be warned, but z is ok
+fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ let CopyWrapper(s) = z; // moved
+ let CopyWrapper(ref t) = y; // not moved
+ let CopyWrapper(_) = y; // still not moved
+
+ assert_eq!(x.0, s);
+ println!("{}", t);
+}
+
+// The following 3 lines should not cause an ICE. See #2831
+trait Bar<'a, A> {}
+impl<'b, T> Bar<'b, T> for T {}
+fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+
+// Also this should not cause an ICE. See #2831
+trait Club<'a, A> {}
+impl<T> Club<'static, T> for T {}
+fn more_fun(_item: impl Club<'static, i32>) {}
+
+fn is_sync<T>(_: T)
+where
+ T: Sync,
+{
+}
+
+fn main() {
+ // This should not cause an ICE either
+ // https://github.com/rust-lang/rust-clippy/issues/3144
+ is_sync(HashSet::<usize>::new());
+}
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
new file mode 100644
index 000000000..38f33c53f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
@@ -0,0 +1,178 @@
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:17:23
+ |
+LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
+ | ^^^^^^ help: consider changing the type to: `&[T]`
+ |
+ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:31:11
+ |
+LL | fn bar(x: String, y: Wrapper) {
+ | ^^^^^^ help: consider changing the type to: `&str`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:31:22
+ |
+LL | fn bar(x: String, y: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:37:71
+ |
+LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
+ | ^ help: consider taking a reference instead: `&V`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:49:18
+ |
+LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:62:24
+ |
+LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:62:36
+ |
+LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
+ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:78:49
+ |
+LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+ | ^ help: consider taking a reference instead: `&T`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:80:18
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^ help: consider taking a reference instead: `&String`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:80:29
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^
+ |
+help: consider changing the type to
+ |
+LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
+ | ~~~~
+help: change `t.clone()` to
+ |
+LL | let _ = t.to_string();
+ | ~~~~~~~~~~~~~
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:80:40
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:80:53
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
+ | ^^^^^^^^
+ |
+help: consider changing the type to
+ |
+LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
+ | ~~~~~~
+help: change `v.clone()` to
+ |
+LL | let _ = v.to_owned();
+ | ~~~~~~~~~~~~
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:93:12
+ |
+LL | s: String,
+ | ^^^^^^ help: consider changing the type to: `&str`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:94:12
+ |
+LL | t: String,
+ | ^^^^^^ help: consider taking a reference instead: `&String`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:103:23
+ |
+LL | fn baz(&self, _u: U, _s: Self) {}
+ | ^ help: consider taking a reference instead: `&U`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:103:30
+ |
+LL | fn baz(&self, _u: U, _s: Self) {}
+ | ^^^^ help: consider taking a reference instead: `&Self`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:125:24
+ |
+LL | fn bar_copy(x: u32, y: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
+ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:131:29
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
+ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:131:45
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
+ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:131:61
+ |
+LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
+ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
+ |
+help: consider marking this type as `Copy`
+ --> $DIR/needless_pass_by_value.rs:123:1
+ |
+LL | struct CopyWrapper(u32);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:143:40
+ |
+LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+ | ^ help: consider taking a reference instead: `&S`
+
+error: this argument is passed by value, but not consumed in the function body
+ --> $DIR/needless_pass_by_value.rs:148:20
+ |
+LL | fn more_fun(_item: impl Club<'static, i32>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
new file mode 100644
index 000000000..78a0e92d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
@@ -0,0 +1,21 @@
+#![crate_type = "proc-macro"]
+#![warn(clippy::needless_pass_by_value)]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(Foo)]
+pub fn foo(_input: TokenStream) -> TokenStream {
+ unimplemented!()
+}
+
+#[proc_macro]
+pub fn bar(_input: TokenStream) -> TokenStream {
+ unimplemented!()
+}
+
+#[proc_macro_attribute]
+pub fn baz(_args: TokenStream, _input: TokenStream) -> TokenStream {
+ unimplemented!()
+}
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed
new file mode 100644
index 000000000..ba9d15e59
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed
@@ -0,0 +1,140 @@
+// run-rustfix
+
+#![warn(clippy::needless_question_mark)]
+#![allow(
+ clippy::needless_return,
+ clippy::unnecessary_unwrap,
+ clippy::upper_case_acronyms,
+ dead_code,
+ unused_must_use
+)]
+#![feature(custom_inner_attributes)]
+
+struct TO {
+ magic: Option<usize>,
+}
+
+struct TR {
+ magic: Result<usize, bool>,
+}
+
+fn simple_option_bad1(to: TO) -> Option<usize> {
+ // return as a statement
+ return to.magic;
+}
+
+// formatting will add a semi-colon, which would make
+// this identical to the test case above
+#[rustfmt::skip]
+fn simple_option_bad2(to: TO) -> Option<usize> {
+ // return as an expression
+ return to.magic
+}
+
+fn simple_option_bad3(to: TO) -> Option<usize> {
+ // block value "return"
+ to.magic
+}
+
+fn simple_option_bad4(to: Option<TO>) -> Option<usize> {
+ // single line closure
+ to.and_then(|t| t.magic)
+}
+
+// formatting this will remove the block brackets, making
+// this test identical to the one above
+#[rustfmt::skip]
+fn simple_option_bad5(to: Option<TO>) -> Option<usize> {
+ // closure with body
+ to.and_then(|t| {
+ t.magic
+ })
+}
+
+fn simple_result_bad1(tr: TR) -> Result<usize, bool> {
+ return tr.magic;
+}
+
+// formatting will add a semi-colon, which would make
+// this identical to the test case above
+#[rustfmt::skip]
+fn simple_result_bad2(tr: TR) -> Result<usize, bool> {
+ return tr.magic
+}
+
+fn simple_result_bad3(tr: TR) -> Result<usize, bool> {
+ tr.magic
+}
+
+fn simple_result_bad4(tr: Result<TR, bool>) -> Result<usize, bool> {
+ tr.and_then(|t| t.magic)
+}
+
+// formatting this will remove the block brackets, making
+// this test identical to the one above
+#[rustfmt::skip]
+fn simple_result_bad5(tr: Result<TR, bool>) -> Result<usize, bool> {
+ tr.and_then(|t| {
+ t.magic
+ })
+}
+
+fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
+ if tr.is_ok() {
+ let t = tr.unwrap();
+ return t.magic;
+ }
+ Err(false)
+}
+
+fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
+where
+ T: From<U>,
+{
+ Ok(x?)
+}
+
+// not quite needless
+fn deref_ref(s: Option<&String>) -> Option<&str> {
+ Some(s?)
+}
+
+fn main() {}
+
+// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+ ($expr:expr) => {
+ || -> _ { Some($expr) }()
+ };
+}
+
+pub fn test1() {
+ let x = Some(3);
+ let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+ ($expr:expr) => {
+ || -> Option<_> { Some($expr) }()
+ };
+}
+
+pub fn test2() {
+ let x = Some(3);
+ let _x = some_and_qmark_in_macro!(x?);
+}
+
+async fn async_option_bad(to: TO) -> Option<usize> {
+ let _ = Some(3);
+ to.magic
+}
+
+async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
+ Some(s?)
+}
+
+async fn async_result_bad(s: TR) -> Result<usize, bool> {
+ s.magic
+}
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs
new file mode 100644
index 000000000..3a6523e8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_question_mark.rs
@@ -0,0 +1,140 @@
+// run-rustfix
+
+#![warn(clippy::needless_question_mark)]
+#![allow(
+ clippy::needless_return,
+ clippy::unnecessary_unwrap,
+ clippy::upper_case_acronyms,
+ dead_code,
+ unused_must_use
+)]
+#![feature(custom_inner_attributes)]
+
+struct TO {
+ magic: Option<usize>,
+}
+
+struct TR {
+ magic: Result<usize, bool>,
+}
+
+fn simple_option_bad1(to: TO) -> Option<usize> {
+ // return as a statement
+ return Some(to.magic?);
+}
+
+// formatting will add a semi-colon, which would make
+// this identical to the test case above
+#[rustfmt::skip]
+fn simple_option_bad2(to: TO) -> Option<usize> {
+ // return as an expression
+ return Some(to.magic?)
+}
+
+fn simple_option_bad3(to: TO) -> Option<usize> {
+ // block value "return"
+ Some(to.magic?)
+}
+
+fn simple_option_bad4(to: Option<TO>) -> Option<usize> {
+ // single line closure
+ to.and_then(|t| Some(t.magic?))
+}
+
+// formatting this will remove the block brackets, making
+// this test identical to the one above
+#[rustfmt::skip]
+fn simple_option_bad5(to: Option<TO>) -> Option<usize> {
+ // closure with body
+ to.and_then(|t| {
+ Some(t.magic?)
+ })
+}
+
+fn simple_result_bad1(tr: TR) -> Result<usize, bool> {
+ return Ok(tr.magic?);
+}
+
+// formatting will add a semi-colon, which would make
+// this identical to the test case above
+#[rustfmt::skip]
+fn simple_result_bad2(tr: TR) -> Result<usize, bool> {
+ return Ok(tr.magic?)
+}
+
+fn simple_result_bad3(tr: TR) -> Result<usize, bool> {
+ Ok(tr.magic?)
+}
+
+fn simple_result_bad4(tr: Result<TR, bool>) -> Result<usize, bool> {
+ tr.and_then(|t| Ok(t.magic?))
+}
+
+// formatting this will remove the block brackets, making
+// this test identical to the one above
+#[rustfmt::skip]
+fn simple_result_bad5(tr: Result<TR, bool>) -> Result<usize, bool> {
+ tr.and_then(|t| {
+ Ok(t.magic?)
+ })
+}
+
+fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
+ if tr.is_ok() {
+ let t = tr.unwrap();
+ return Ok(t.magic?);
+ }
+ Err(false)
+}
+
+fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
+where
+ T: From<U>,
+{
+ Ok(x?)
+}
+
+// not quite needless
+fn deref_ref(s: Option<&String>) -> Option<&str> {
+ Some(s?)
+}
+
+fn main() {}
+
+// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+ ($expr:expr) => {
+ || -> _ { Some($expr) }()
+ };
+}
+
+pub fn test1() {
+ let x = Some(3);
+ let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+ ($expr:expr) => {
+ || -> Option<_> { Some(Some($expr)?) }()
+ };
+}
+
+pub fn test2() {
+ let x = Some(3);
+ let _x = some_and_qmark_in_macro!(x?);
+}
+
+async fn async_option_bad(to: TO) -> Option<usize> {
+ let _ = Some(3);
+ Some(to.magic?)
+}
+
+async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
+ Some(s?)
+}
+
+async fn async_result_bad(s: TR) -> Result<usize, bool> {
+ Ok(s.magic?)
+}
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr
new file mode 100644
index 000000000..f8308e24e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr
@@ -0,0 +1,93 @@
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:23:12
+ |
+LL | return Some(to.magic?);
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
+ |
+ = note: `-D clippy::needless-question-mark` implied by `-D warnings`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:31:12
+ |
+LL | return Some(to.magic?)
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:36:5
+ |
+LL | Some(to.magic?)
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:41:21
+ |
+LL | to.and_then(|t| Some(t.magic?))
+ | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:50:9
+ |
+LL | Some(t.magic?)
+ | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:55:12
+ |
+LL | return Ok(tr.magic?);
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:62:12
+ |
+LL | return Ok(tr.magic?)
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:66:5
+ |
+LL | Ok(tr.magic?)
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:70:21
+ |
+LL | tr.and_then(|t| Ok(t.magic?))
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:78:9
+ |
+LL | Ok(t.magic?)
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:85:16
+ |
+LL | return Ok(t.magic?);
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:120:27
+ |
+LL | || -> Option<_> { Some(Some($expr)?) }()
+ | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
+...
+LL | let _x = some_and_qmark_in_macro!(x?);
+ | ---------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:131:5
+ |
+LL | Some(to.magic?)
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
+
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:139:5
+ |
+LL | Ok(s.magic?)
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs
new file mode 100644
index 000000000..3fce34367
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_range_loop.rs
@@ -0,0 +1,95 @@
+#![warn(clippy::needless_range_loop)]
+
+static STATIC: [usize; 4] = [0, 1, 8, 16];
+const CONST: [usize; 4] = [0, 1, 8, 16];
+const MAX_LEN: usize = 42;
+
+fn main() {
+ let mut vec = vec![1, 2, 3, 4];
+ let vec2 = vec![1, 2, 3, 4];
+ for i in 0..vec.len() {
+ println!("{}", vec[i]);
+ }
+
+ for i in 0..vec.len() {
+ let i = 42; // make a different `i`
+ println!("{}", vec[i]); // ok, not the `i` of the for-loop
+ }
+
+ for i in 0..vec.len() {
+ let _ = vec[i];
+ }
+
+ // ICE #746
+ for j in 0..4 {
+ println!("{:?}", STATIC[j]);
+ }
+
+ for j in 0..4 {
+ println!("{:?}", CONST[j]);
+ }
+
+ for i in 0..vec.len() {
+ println!("{} {}", vec[i], i);
+ }
+ for i in 0..vec.len() {
+ // not an error, indexing more than one variable
+ println!("{} {}", vec[i], vec2[i]);
+ }
+
+ for i in 0..vec.len() {
+ println!("{}", vec2[i]);
+ }
+
+ for i in 5..vec.len() {
+ println!("{}", vec[i]);
+ }
+
+ for i in 0..MAX_LEN {
+ println!("{}", vec[i]);
+ }
+
+ for i in 0..=MAX_LEN {
+ println!("{}", vec[i]);
+ }
+
+ for i in 5..10 {
+ println!("{}", vec[i]);
+ }
+
+ for i in 5..=10 {
+ println!("{}", vec[i]);
+ }
+
+ for i in 5..vec.len() {
+ println!("{} {}", vec[i], i);
+ }
+
+ for i in 5..10 {
+ println!("{} {}", vec[i], i);
+ }
+
+ // #2542
+ for i in 0..vec.len() {
+ vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i));
+ }
+
+ // #3788
+ let test = Test {
+ inner: vec![1, 2, 3, 4],
+ };
+ for i in 0..2 {
+ println!("{}", test[i]);
+ }
+}
+
+struct Test {
+ inner: Vec<usize>,
+}
+
+impl std::ops::Index<usize> for Test {
+ type Output = usize;
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.inner[index]
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr
new file mode 100644
index 000000000..a86cc69df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr
@@ -0,0 +1,157 @@
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:10:14
+ |
+LL | for i in 0..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-range-loop` implied by `-D warnings`
+help: consider using an iterator
+ |
+LL | for <item> in &vec {
+ | ~~~~~~ ~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:19:14
+ |
+LL | for i in 0..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &vec {
+ | ~~~~~~ ~~~~
+
+error: the loop variable `j` is only used to index `STATIC`
+ --> $DIR/needless_range_loop.rs:24:14
+ |
+LL | for j in 0..4 {
+ | ^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &STATIC {
+ | ~~~~~~ ~~~~~~~
+
+error: the loop variable `j` is only used to index `CONST`
+ --> $DIR/needless_range_loop.rs:28:14
+ |
+LL | for j in 0..4 {
+ | ^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &CONST {
+ | ~~~~~~ ~~~~~~
+
+error: the loop variable `i` is used to index `vec`
+ --> $DIR/needless_range_loop.rs:32:14
+ |
+LL | for i in 0..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for (i, <item>) in vec.iter().enumerate() {
+ | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec2`
+ --> $DIR/needless_range_loop.rs:40:14
+ |
+LL | for i in 0..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec2.iter().take(vec.len()) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:44:14
+ |
+LL | for i in 5..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter().skip(5) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:48:14
+ |
+LL | for i in 0..MAX_LEN {
+ | ^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter().take(MAX_LEN) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:52:14
+ |
+LL | for i in 0..=MAX_LEN {
+ | ^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter().take(MAX_LEN + 1) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:56:14
+ |
+LL | for i in 5..10 {
+ | ^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter().take(10).skip(5) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop.rs:60:14
+ |
+LL | for i in 5..=10 {
+ | ^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter().take(10 + 1).skip(5) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is used to index `vec`
+ --> $DIR/needless_range_loop.rs:64:14
+ |
+LL | for i in 5..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for (i, <item>) in vec.iter().enumerate().skip(5) {
+ | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is used to index `vec`
+ --> $DIR/needless_range_loop.rs:68:14
+ |
+LL | for i in 5..10 {
+ | ^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
+ | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is used to index `vec`
+ --> $DIR/needless_range_loop.rs:73:14
+ |
+LL | for i in 0..vec.len() {
+ | ^^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for (i, <item>) in vec.iter_mut().enumerate() {
+ | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.rs b/src/tools/clippy/tests/ui/needless_range_loop2.rs
new file mode 100644
index 000000000..7633316e0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_range_loop2.rs
@@ -0,0 +1,109 @@
+#![warn(clippy::needless_range_loop)]
+
+fn calc_idx(i: usize) -> usize {
+ (i + i + 20) % 4
+}
+
+fn main() {
+ let ns = vec![2, 3, 5, 7];
+
+ for i in 3..10 {
+ println!("{}", ns[i]);
+ }
+
+ for i in 3..10 {
+ println!("{}", ns[i % 4]);
+ }
+
+ for i in 3..10 {
+ println!("{}", ns[i % ns.len()]);
+ }
+
+ for i in 3..10 {
+ println!("{}", ns[calc_idx(i)]);
+ }
+
+ for i in 3..10 {
+ println!("{}", ns[calc_idx(i) % 4]);
+ }
+
+ let mut ms = vec![1, 2, 3, 4, 5, 6];
+ for i in 0..ms.len() {
+ ms[i] *= 2;
+ }
+ assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
+
+ let mut ms = vec![1, 2, 3, 4, 5, 6];
+ for i in 0..ms.len() {
+ let x = &mut ms[i];
+ *x *= 2;
+ }
+ assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
+
+ let g = vec![1, 2, 3, 4, 5, 6];
+ let glen = g.len();
+ for i in 0..glen {
+ let x: u32 = g[i + 1..].iter().sum();
+ println!("{}", g[i] + x);
+ }
+ assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
+
+ let mut g = vec![1, 2, 3, 4, 5, 6];
+ let glen = g.len();
+ for i in 0..glen {
+ g[i] = g[i + 1..].iter().sum();
+ }
+ assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
+
+ let x = 5;
+ let mut vec = vec![0; 9];
+
+ for i in x..x + 4 {
+ vec[i] += 1;
+ }
+
+ let x = 5;
+ let mut vec = vec![0; 10];
+
+ for i in x..=x + 4 {
+ vec[i] += 1;
+ }
+
+ let arr = [1, 2, 3];
+
+ for i in 0..3 {
+ println!("{}", arr[i]);
+ }
+
+ for i in 0..2 {
+ println!("{}", arr[i]);
+ }
+
+ for i in 1..3 {
+ println!("{}", arr[i]);
+ }
+
+ // Fix #5945
+ let mut vec = vec![1, 2, 3, 4];
+ for i in 0..vec.len() - 1 {
+ vec[i] += 1;
+ }
+ let mut vec = vec![1, 2, 3, 4];
+ for i in vec.len() - 3..vec.len() {
+ vec[i] += 1;
+ }
+ let mut vec = vec![1, 2, 3, 4];
+ for i in vec.len() - 3..vec.len() - 1 {
+ vec[i] += 1;
+ }
+}
+
+mod issue2277 {
+ pub fn example(list: &[[f64; 3]]) {
+ let mut x: [f64; 3] = [10.; 3];
+
+ for i in 0..3 {
+ x[i] = list.iter().map(|item| item[i]).sum::<f64>();
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
new file mode 100644
index 000000000..1e6ec5e66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr
@@ -0,0 +1,91 @@
+error: the loop variable `i` is only used to index `ns`
+ --> $DIR/needless_range_loop2.rs:10:14
+ |
+LL | for i in 3..10 {
+ | ^^^^^
+ |
+ = note: `-D clippy::needless-range-loop` implied by `-D warnings`
+help: consider using an iterator
+ |
+LL | for <item> in ns.iter().take(10).skip(3) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `ms`
+ --> $DIR/needless_range_loop2.rs:31:14
+ |
+LL | for i in 0..ms.len() {
+ | ^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &mut ms {
+ | ~~~~~~ ~~~~~~~
+
+error: the loop variable `i` is only used to index `ms`
+ --> $DIR/needless_range_loop2.rs:37:14
+ |
+LL | for i in 0..ms.len() {
+ | ^^^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &mut ms {
+ | ~~~~~~ ~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop2.rs:61:14
+ |
+LL | for i in x..x + 4 {
+ | ^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter_mut().skip(x).take(4) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `vec`
+ --> $DIR/needless_range_loop2.rs:68:14
+ |
+LL | for i in x..=x + 4 {
+ | ^^^^^^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in vec.iter_mut().skip(x).take(4 + 1) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `arr`
+ --> $DIR/needless_range_loop2.rs:74:14
+ |
+LL | for i in 0..3 {
+ | ^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in &arr {
+ | ~~~~~~ ~~~~
+
+error: the loop variable `i` is only used to index `arr`
+ --> $DIR/needless_range_loop2.rs:78:14
+ |
+LL | for i in 0..2 {
+ | ^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in arr.iter().take(2) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~
+
+error: the loop variable `i` is only used to index `arr`
+ --> $DIR/needless_range_loop2.rs:82:14
+ |
+LL | for i in 1..3 {
+ | ^^^^
+ |
+help: consider using an iterator
+ |
+LL | for <item> in arr.iter().skip(1) {
+ | ~~~~~~ ~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
new file mode 100644
index 000000000..0bc0d0011
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -0,0 +1,240 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![feature(let_else)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+use std::cell::RefCell;
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+fn test_no_semicolon() -> bool {
+ true
+}
+
+fn test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+fn test_macro_call() -> i32 {
+ the_answer!()
+}
+
+fn test_void_fun() {
+
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => (),
+ }
+}
+
+fn test_nested_match(x: u32) {
+ match x {
+ 0 => (),
+ 1 => {
+ let _ = 42;
+
+ },
+ _ => (),
+ }
+}
+
+fn temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| {})
+ }
+
+ fn test_closure() {
+ let _ = || {
+
+ };
+ let _ = || {};
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+async fn async_test_no_semicolon() -> bool {
+ true
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ the_answer!()
+}
+
+async fn async_test_void_fun() {
+
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+
+ } else {
+
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => (),
+ }
+}
+
+async fn async_temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn needless_return_macro() -> String {
+ let _ = "foo";
+ let _ = "bar";
+ format!("Hello {}", "world!")
+}
+
+fn check_expect() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ #[expect(clippy::needless_return)]
+ return true;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
new file mode 100644
index 000000000..eb9f72e8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -0,0 +1,240 @@
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![feature(let_else)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+use std::cell::RefCell;
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+fn test_no_semicolon() -> bool {
+ return true;
+}
+
+fn test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+ return;
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+fn test_nested_match(x: u32) {
+ match x {
+ 0 => (),
+ 1 => {
+ let _ = 42;
+ return;
+ },
+ _ => return,
+ }
+}
+
+fn temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| return)
+ }
+
+ fn test_closure() {
+ let _ = || {
+ return;
+ };
+ let _ = || return;
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| return Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+ return true;
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+ return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+async fn async_temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn needless_return_macro() -> String {
+ let _ = "foo";
+ let _ = "bar";
+ return format!("Hello {}", "world!");
+}
+
+fn check_expect() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ #[expect(clippy::needless_return)]
+ return true;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
new file mode 100644
index 000000000..83ff07638
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -0,0 +1,226 @@
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:27:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+ |
+ = note: `-D clippy::needless-return` implied by `-D warnings`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:31:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:36:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:38:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:44:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:46:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:53:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:55:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:59:5
+ |
+LL | return the_answer!();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:63:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:68:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:70:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:77:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with a unit value: `()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:86:13
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:88:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with a unit value: `()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:101:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:103:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:125:32
+ |
+LL | bar.unwrap_or_else(|_| return)
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:130:13
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:132:20
+ |
+LL | let _ = || return;
+ | ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:138:32
+ |
+LL | res.unwrap_or_else(|_| return Foo)
+ | ^^^^^^^^^^ help: remove `return`: `Foo`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:147:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:151:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:156:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:158:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:164:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:166:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:173:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:175:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:179:5
+ |
+LL | return the_answer!();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:183:5
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:188:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:190:9
+ |
+LL | return;
+ | ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:197:14
+ |
+LL | _ => return,
+ | ^^^^^^ help: replace `return` with a unit value: `()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:210:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:212:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:228:5
+ |
+LL | return format!("Hello {}", "world!");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")`
+
+error: aborting due to 37 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_splitn.fixed b/src/tools/clippy/tests/ui/needless_splitn.fixed
new file mode 100644
index 000000000..61f5fc4e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let str = "key=value=end";
+ let _ = str.split('=').next();
+ let _ = str.split('=').nth(0);
+ let _ = str.splitn(2, '=').nth(1);
+ let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.split('=').next_tuple().unwrap();
+ let _: Vec<&str> = str.splitn(3, '=').collect();
+
+ let _ = str.rsplit('=').next();
+ let _ = str.rsplit('=').nth(0);
+ let _ = str.rsplitn(2, '=').nth(1);
+ let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.rsplit('=').next_tuple().unwrap();
+
+ let _ = str.split('=').next();
+ let _ = str.split('=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.split('=').next()?;
+ let _ = s.split('=').nth(0)?;
+ let _ = s.rsplit('=').next()?;
+ let _ = s.rsplit('=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".split('=').nth(0).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.rs b/src/tools/clippy/tests/ui/needless_splitn.rs
new file mode 100644
index 000000000..71d9a7077
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.rs
@@ -0,0 +1,47 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let str = "key=value=end";
+ let _ = str.splitn(2, '=').next();
+ let _ = str.splitn(2, '=').nth(0);
+ let _ = str.splitn(2, '=').nth(1);
+ let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+ let _: Vec<&str> = str.splitn(3, '=').collect();
+
+ let _ = str.rsplitn(2, '=').next();
+ let _ = str.rsplitn(2, '=').nth(0);
+ let _ = str.rsplitn(2, '=').nth(1);
+ let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+
+ let _ = str.splitn(5, '=').next();
+ let _ = str.splitn(5, '=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.splitn(2, '=').next()?;
+ let _ = s.splitn(2, '=').nth(0)?;
+ let _ = s.rsplitn(2, '=').next()?;
+ let _ = s.rsplitn(2, '=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.stderr b/src/tools/clippy/tests/ui/needless_splitn.stderr
new file mode 100644
index 000000000..f112b29e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.stderr
@@ -0,0 +1,82 @@
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:15:13
+ |
+LL | let _ = str.splitn(2, '=').next();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+ |
+ = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:16:13
+ |
+LL | let _ = str.splitn(2, '=').nth(0);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:19:18
+ |
+LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:22:13
+ |
+LL | let _ = str.rsplitn(2, '=').next();
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:23:13
+ |
+LL | let _ = str.rsplitn(2, '=').nth(0);
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:26:18
+ |
+LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:28:13
+ |
+LL | let _ = str.splitn(5, '=').next();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:29:13
+ |
+LL | let _ = str.splitn(5, '=').nth(3);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:35:13
+ |
+LL | let _ = s.splitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:36:13
+ |
+LL | let _ = s.splitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:37:13
+ |
+LL | let _ = s.rsplitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:38:13
+ |
+LL | let _ = s.rsplitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:46:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs
new file mode 100644
index 000000000..b93ff048a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_update.rs
@@ -0,0 +1,25 @@
+#![warn(clippy::needless_update)]
+#![allow(clippy::no_effect)]
+
+struct S {
+ pub a: i32,
+ pub b: i32,
+}
+
+#[non_exhaustive]
+struct T {
+ pub x: i32,
+ pub y: i32,
+}
+
+fn main() {
+ let base = S { a: 0, b: 0 };
+ S { ..base }; // no error
+ S { a: 1, ..base }; // no error
+ S { a: 1, b: 1, ..base };
+
+ let base = T { x: 0, y: 0 };
+ T { ..base }; // no error
+ T { x: 1, ..base }; // no error
+ T { x: 1, y: 1, ..base }; // no error
+}
diff --git a/src/tools/clippy/tests/ui/needless_update.stderr b/src/tools/clippy/tests/ui/needless_update.stderr
new file mode 100644
index 000000000..b154b3b30
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_update.stderr
@@ -0,0 +1,10 @@
+error: struct update has no effect, all the fields in the struct have already been specified
+ --> $DIR/needless_update.rs:19:23
+ |
+LL | S { a: 1, b: 1, ..base };
+ | ^^^^
+ |
+ = note: `-D clippy::needless-update` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
new file mode 100644
index 000000000..2d392c593
--- /dev/null
+++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
@@ -0,0 +1,62 @@
+//! This test case utilizes `f64` an easy example for `PartialOrd` only types
+//! but the lint itself actually validates any expression where the left
+//! operand implements `PartialOrd` but not `Ord`.
+
+use std::cmp::Ordering;
+
+#[allow(clippy::unnested_or_patterns, clippy::match_like_matches_macro)]
+#[warn(clippy::neg_cmp_op_on_partial_ord)]
+fn main() {
+ let a_value = 1.0;
+ let another_value = 7.0;
+
+ // --- Bad ---
+
+ // Not Less but potentially Greater, Equal or Uncomparable.
+ let _not_less = !(a_value < another_value);
+
+ // Not Less or Equal but potentially Greater or Uncomparable.
+ let _not_less_or_equal = !(a_value <= another_value);
+
+ // Not Greater but potentially Less, Equal or Uncomparable.
+ let _not_greater = !(a_value > another_value);
+
+ // Not Greater or Equal but potentially Less or Uncomparable.
+ let _not_greater_or_equal = !(a_value >= another_value);
+
+ // --- Good ---
+
+ let _not_less = match a_value.partial_cmp(&another_value) {
+ None | Some(Ordering::Greater) | Some(Ordering::Equal) => true,
+ _ => false,
+ };
+ let _not_less_or_equal = match a_value.partial_cmp(&another_value) {
+ None | Some(Ordering::Greater) => true,
+ _ => false,
+ };
+ let _not_greater = match a_value.partial_cmp(&another_value) {
+ None | Some(Ordering::Less) | Some(Ordering::Equal) => true,
+ _ => false,
+ };
+ let _not_greater_or_equal = match a_value.partial_cmp(&another_value) {
+ None | Some(Ordering::Less) => true,
+ _ => false,
+ };
+
+ // --- Should not trigger ---
+
+ let _ = a_value < another_value;
+ let _ = a_value <= another_value;
+ let _ = a_value > another_value;
+ let _ = a_value >= another_value;
+
+ // --- regression tests ---
+
+ // Issue 2856: False positive on assert!()
+ //
+ // The macro always negates the result of the given comparison in its
+ // internal check which automatically triggered the lint. As it's an
+ // external macro there was no chance to do anything about it which led
+ // to an exempting of all external macros.
+ assert!(a_value < another_value);
+}
diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr
new file mode 100644
index 000000000..c78560007
--- /dev/null
+++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr
@@ -0,0 +1,28 @@
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
+ --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21
+ |
+LL | let _not_less = !(a_value < another_value);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
+
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
+ --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30
+ |
+LL | let _not_less_or_equal = !(a_value <= another_value);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
+ --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24
+ |
+LL | let _not_greater = !(a_value > another_value);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
+ --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33
+ |
+LL | let _not_greater_or_equal = !(a_value >= another_value);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed
new file mode 100644
index 000000000..58ab9e856
--- /dev/null
+++ b/src/tools/clippy/tests/ui/neg_multiply.fixed
@@ -0,0 +1,48 @@
+// run-rustfix
+#![warn(clippy::neg_multiply)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)]
+#![allow(unused)]
+
+use std::ops::Mul;
+
+struct X;
+
+impl Mul<isize> for X {
+ type Output = X;
+
+ fn mul(self, _r: isize) -> Self {
+ self
+ }
+}
+
+impl Mul<X> for isize {
+ type Output = X;
+
+ fn mul(self, _r: X) -> X {
+ X
+ }
+}
+
+fn main() {
+ let x = 0;
+
+ -x;
+
+ -x;
+
+ 100 + -x;
+
+ -(100 + x);
+
+ -17;
+
+ 0xcafe | -0xff00;
+
+ -(3_usize as i32);
+ -(3_usize as i32);
+
+ -1 * -1; // should be ok
+
+ X * -1; // should be ok
+ -1 * X; // should also be ok
+}
diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs
new file mode 100644
index 000000000..581290dc7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/neg_multiply.rs
@@ -0,0 +1,48 @@
+// run-rustfix
+#![warn(clippy::neg_multiply)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)]
+#![allow(unused)]
+
+use std::ops::Mul;
+
+struct X;
+
+impl Mul<isize> for X {
+ type Output = X;
+
+ fn mul(self, _r: isize) -> Self {
+ self
+ }
+}
+
+impl Mul<X> for isize {
+ type Output = X;
+
+ fn mul(self, _r: X) -> X {
+ X
+ }
+}
+
+fn main() {
+ let x = 0;
+
+ x * -1;
+
+ -1 * x;
+
+ 100 + x * -1;
+
+ (100 + x) * -1;
+
+ -1 * 17;
+
+ 0xcafe | 0xff00 * -1;
+
+ 3_usize as i32 * -1;
+ (3_usize as i32) * -1;
+
+ -1 * -1; // should be ok
+
+ X * -1; // should be ok
+ -1 * X; // should also be ok
+}
diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr
new file mode 100644
index 000000000..388ef29eb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/neg_multiply.stderr
@@ -0,0 +1,52 @@
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:29:5
+ |
+LL | x * -1;
+ | ^^^^^^ help: consider using: `-x`
+ |
+ = note: `-D clippy::neg-multiply` implied by `-D warnings`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:31:5
+ |
+LL | -1 * x;
+ | ^^^^^^ help: consider using: `-x`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:33:11
+ |
+LL | 100 + x * -1;
+ | ^^^^^^ help: consider using: `-x`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:35:5
+ |
+LL | (100 + x) * -1;
+ | ^^^^^^^^^^^^^^ help: consider using: `-(100 + x)`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:37:5
+ |
+LL | -1 * 17;
+ | ^^^^^^^ help: consider using: `-17`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:39:14
+ |
+LL | 0xcafe | 0xff00 * -1;
+ | ^^^^^^^^^^^ help: consider using: `-0xff00`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:41:5
+ |
+LL | 3_usize as i32 * -1;
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)`
+
+error: this multiplication by -1 can be written more succinctly
+ --> $DIR/neg_multiply.rs:42:5
+ |
+LL | (3_usize as i32) * -1;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs
new file mode 100644
index 000000000..0a21589dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/never_loop.rs
@@ -0,0 +1,221 @@
+#![allow(
+ clippy::single_match,
+ unused_assignments,
+ unused_variables,
+ clippy::while_immutable_condition
+)]
+
+fn test1() {
+ let mut x = 0;
+ loop {
+ // clippy::never_loop
+ x += 1;
+ if x == 1 {
+ return;
+ }
+ break;
+ }
+}
+
+fn test2() {
+ let mut x = 0;
+ loop {
+ x += 1;
+ if x == 1 {
+ break;
+ }
+ }
+}
+
+fn test3() {
+ let mut x = 0;
+ loop {
+ // never loops
+ x += 1;
+ break;
+ }
+}
+
+fn test4() {
+ let mut x = 1;
+ loop {
+ x += 1;
+ match x {
+ 5 => return,
+ _ => (),
+ }
+ }
+}
+
+fn test5() {
+ let i = 0;
+ loop {
+ // never loops
+ while i == 0 {
+ // never loops
+ break;
+ }
+ return;
+ }
+}
+
+fn test6() {
+ let mut x = 0;
+ 'outer: loop {
+ x += 1;
+ loop {
+ // never loops
+ if x == 5 {
+ break;
+ }
+ continue 'outer;
+ }
+ return;
+ }
+}
+
+fn test7() {
+ let mut x = 0;
+ loop {
+ x += 1;
+ match x {
+ 1 => continue,
+ _ => (),
+ }
+ return;
+ }
+}
+
+fn test8() {
+ let mut x = 0;
+ loop {
+ x += 1;
+ match x {
+ 5 => return,
+ _ => continue,
+ }
+ }
+}
+
+fn test9() {
+ let x = Some(1);
+ while let Some(y) = x {
+ // never loops
+ return;
+ }
+}
+
+fn test10() {
+ for x in 0..10 {
+ // never loops
+ match x {
+ 1 => break,
+ _ => return,
+ }
+ }
+}
+
+fn test11<F: FnMut() -> i32>(mut f: F) {
+ loop {
+ return match f() {
+ 1 => continue,
+ _ => (),
+ };
+ }
+}
+
+pub fn test12(a: bool, b: bool) {
+ 'label: loop {
+ loop {
+ if a {
+ continue 'label;
+ }
+ if b {
+ break;
+ }
+ }
+ break;
+ }
+}
+
+pub fn test13() {
+ let mut a = true;
+ loop {
+ // infinite loop
+ while a {
+ if true {
+ a = false;
+ continue;
+ }
+ return;
+ }
+ }
+}
+
+pub fn test14() {
+ let mut a = true;
+ 'outer: while a {
+ // never loops
+ while a {
+ if a {
+ a = false;
+ continue;
+ }
+ }
+ break 'outer;
+ }
+}
+
+// Issue #1991: the outer loop should not warn.
+pub fn test15() {
+ 'label: loop {
+ while false {
+ break 'label;
+ }
+ }
+}
+
+// Issue #4058: `continue` in `break` expression
+pub fn test16() {
+ let mut n = 1;
+ loop {
+ break if n != 5 {
+ n += 1;
+ continue;
+ };
+ }
+}
+
+// Issue #9001: `continue` in struct expression fields
+pub fn test17() {
+ struct Foo {
+ f: (),
+ }
+
+ let mut n = 0;
+ let _ = loop {
+ break Foo {
+ f: if n < 5 {
+ n += 1;
+ continue;
+ },
+ };
+ };
+}
+
+fn main() {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+ test10();
+ test11(|| 0);
+ test12(true, false);
+ test13();
+ test14();
+}
diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr
new file mode 100644
index 000000000..f49b23924
--- /dev/null
+++ b/src/tools/clippy/tests/ui/never_loop.stderr
@@ -0,0 +1,105 @@
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:10:5
+ |
+LL | / loop {
+LL | | // clippy::never_loop
+LL | | x += 1;
+LL | | if x == 1 {
+... |
+LL | | break;
+LL | | }
+ | |_____^
+ |
+ = note: `#[deny(clippy::never_loop)]` on by default
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:32:5
+ |
+LL | / loop {
+LL | | // never loops
+LL | | x += 1;
+LL | | break;
+LL | | }
+ | |_____^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:52:5
+ |
+LL | / loop {
+LL | | // never loops
+LL | | while i == 0 {
+LL | | // never loops
+... |
+LL | | return;
+LL | | }
+ | |_____^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:54:9
+ |
+LL | / while i == 0 {
+LL | | // never loops
+LL | | break;
+LL | | }
+ | |_________^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:66:9
+ |
+LL | / loop {
+LL | | // never loops
+LL | | if x == 5 {
+LL | | break;
+LL | | }
+LL | | continue 'outer;
+LL | | }
+ | |_________^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:102:5
+ |
+LL | / while let Some(y) = x {
+LL | | // never loops
+LL | | return;
+LL | | }
+ | |_____^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:109:5
+ |
+LL | / for x in 0..10 {
+LL | | // never loops
+LL | | match x {
+LL | | 1 => break,
+LL | | _ => return,
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: if you need the first element of the iterator, try writing
+ |
+LL | if let Some(x) = (0..10).next() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:157:5
+ |
+LL | / 'outer: while a {
+LL | | // never loops
+LL | | while a {
+LL | | if a {
+... |
+LL | | break 'outer;
+LL | | }
+ | |_____^
+
+error: this loop never actually loops
+ --> $DIR/never_loop.rs:172:9
+ |
+LL | / while false {
+LL | | break 'label;
+LL | | }
+ | |_________^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
new file mode 100644
index 000000000..2f315ffe2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -0,0 +1,352 @@
+#![warn(clippy::new_ret_no_self)]
+#![allow(dead_code)]
+
+fn main() {}
+
+trait R {
+ type Item;
+}
+
+trait Q {
+ type Item;
+ type Item2;
+}
+
+struct S;
+
+impl R for S {
+ type Item = Self;
+}
+
+impl S {
+ // should not trigger the lint
+ pub fn new() -> impl R<Item = Self> {
+ S
+ }
+}
+
+struct S2;
+
+impl R for S2 {
+ type Item = Self;
+}
+
+impl S2 {
+ // should not trigger the lint
+ pub fn new(_: String) -> impl R<Item = Self> {
+ S2
+ }
+}
+
+struct S3;
+
+impl R for S3 {
+ type Item = u32;
+}
+
+impl S3 {
+ // should trigger the lint
+ pub fn new(_: String) -> impl R<Item = u32> {
+ S3
+ }
+}
+
+struct S4;
+
+impl Q for S4 {
+ type Item = u32;
+ type Item2 = Self;
+}
+
+impl S4 {
+ // should not trigger the lint
+ pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
+ S4
+ }
+}
+
+struct T;
+
+impl T {
+ // should not trigger lint
+ pub fn new() -> Self {
+ unimplemented!();
+ }
+}
+
+struct U;
+
+impl U {
+ // should trigger lint
+ pub fn new() -> u32 {
+ unimplemented!();
+ }
+}
+
+struct V;
+
+impl V {
+ // should trigger lint
+ pub fn new(_: String) -> u32 {
+ unimplemented!();
+ }
+}
+
+struct TupleReturnerOk;
+
+impl TupleReturnerOk {
+ // should not trigger lint
+ pub fn new() -> (Self, u32) {
+ unimplemented!();
+ }
+}
+
+struct TupleReturnerOk2;
+
+impl TupleReturnerOk2 {
+ // should not trigger lint (it doesn't matter which element in the tuple is Self)
+ pub fn new() -> (u32, Self) {
+ unimplemented!();
+ }
+}
+
+struct TupleReturnerOk3;
+
+impl TupleReturnerOk3 {
+ // should not trigger lint (tuple can contain multiple Self)
+ pub fn new() -> (Self, Self) {
+ unimplemented!();
+ }
+}
+
+struct TupleReturnerBad;
+
+impl TupleReturnerBad {
+ // should trigger lint
+ pub fn new() -> (u32, u32) {
+ unimplemented!();
+ }
+}
+
+struct MutPointerReturnerOk;
+
+impl MutPointerReturnerOk {
+ // should not trigger lint
+ pub fn new() -> *mut Self {
+ unimplemented!();
+ }
+}
+
+struct ConstPointerReturnerOk2;
+
+impl ConstPointerReturnerOk2 {
+ // should not trigger lint
+ pub fn new() -> *const Self {
+ unimplemented!();
+ }
+}
+
+struct MutPointerReturnerBad;
+
+impl MutPointerReturnerBad {
+ // should trigger lint
+ pub fn new() -> *mut V {
+ unimplemented!();
+ }
+}
+
+struct GenericReturnerOk;
+
+impl GenericReturnerOk {
+ // should not trigger lint
+ pub fn new() -> Option<Self> {
+ unimplemented!();
+ }
+}
+
+struct GenericReturnerBad;
+
+impl GenericReturnerBad {
+ // should trigger lint
+ pub fn new() -> Option<u32> {
+ unimplemented!();
+ }
+}
+
+struct NestedReturnerOk;
+
+impl NestedReturnerOk {
+ // should not trigger lint
+ pub fn new() -> (Option<Self>, u32) {
+ unimplemented!();
+ }
+}
+
+struct NestedReturnerOk2;
+
+impl NestedReturnerOk2 {
+ // should not trigger lint
+ pub fn new() -> ((Self, u32), u32) {
+ unimplemented!();
+ }
+}
+
+struct NestedReturnerOk3;
+
+impl NestedReturnerOk3 {
+ // should not trigger lint
+ pub fn new() -> Option<(Self, u32)> {
+ unimplemented!();
+ }
+}
+
+struct WithLifetime<'a> {
+ cat: &'a str,
+}
+
+impl<'a> WithLifetime<'a> {
+ // should not trigger the lint, because the lifetimes are different
+ pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> {
+ unimplemented!();
+ }
+}
+
+mod issue5435 {
+ struct V;
+
+ pub trait TraitRetSelf {
+ // should not trigger lint
+ fn new() -> Self;
+ }
+
+ pub trait TraitRet {
+ // should trigger lint as we are in trait definition
+ fn new() -> String;
+ }
+ pub struct StructRet;
+ impl TraitRet for StructRet {
+ // should not trigger lint as we are in the impl block
+ fn new() -> String {
+ unimplemented!();
+ }
+ }
+
+ pub trait TraitRet2 {
+ // should trigger lint
+ fn new(_: String) -> String;
+ }
+
+ trait TupleReturnerOk {
+ // should not trigger lint
+ fn new() -> (Self, u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerOk2 {
+ // should not trigger lint (it doesn't matter which element in the tuple is Self)
+ fn new() -> (u32, Self)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerOk3 {
+ // should not trigger lint (tuple can contain multiple Self)
+ fn new() -> (Self, Self)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerBad {
+ // should trigger lint
+ fn new() -> (u32, u32) {
+ unimplemented!();
+ }
+ }
+
+ trait MutPointerReturnerOk {
+ // should not trigger lint
+ fn new() -> *mut Self
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait ConstPointerReturnerOk2 {
+ // should not trigger lint
+ fn new() -> *const Self
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait MutPointerReturnerBad {
+ // should trigger lint
+ fn new() -> *mut V {
+ unimplemented!();
+ }
+ }
+
+ trait GenericReturnerOk {
+ // should not trigger lint
+ fn new() -> Option<Self>
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk {
+ // should not trigger lint
+ fn new() -> (Option<Self>, u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk2 {
+ // should not trigger lint
+ fn new() -> ((Self, u32), u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk3 {
+ // should not trigger lint
+ fn new() -> Option<(Self, u32)>
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+}
+
+// issue #1724
+struct RetOtherSelf<T>(T);
+struct RetOtherSelfWrapper<T>(T);
+
+impl RetOtherSelf<T> {
+ fn new(t: T) -> RetOtherSelf<RetOtherSelfWrapper<T>> {
+ RetOtherSelf(RetOtherSelfWrapper(t))
+ }
+}
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
new file mode 100644
index 000000000..8217bc618
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
@@ -0,0 +1,80 @@
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:49:5
+ |
+LL | / pub fn new(_: String) -> impl R<Item = u32> {
+LL | | S3
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:81:5
+ |
+LL | / pub fn new() -> u32 {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:90:5
+ |
+LL | / pub fn new(_: String) -> u32 {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:126:5
+ |
+LL | / pub fn new() -> (u32, u32) {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:153:5
+ |
+LL | / pub fn new() -> *mut V {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:171:5
+ |
+LL | / pub fn new() -> Option<u32> {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:224:9
+ |
+LL | fn new() -> String;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:236:9
+ |
+LL | fn new(_: String) -> String;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:271:9
+ |
+LL | / fn new() -> (u32, u32) {
+LL | | unimplemented!();
+LL | | }
+ | |_________^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:298:9
+ |
+LL | / fn new() -> *mut V {
+LL | | unimplemented!();
+LL | | }
+ | |_________^
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs
new file mode 100644
index 000000000..65809023f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_without_default.rs
@@ -0,0 +1,228 @@
+#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
+#![warn(clippy::new_without_default)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn new() -> Foo {
+ Foo
+ }
+}
+
+pub struct Bar;
+
+impl Bar {
+ pub fn new() -> Self {
+ Bar
+ }
+}
+
+pub struct Ok;
+
+impl Ok {
+ pub fn new() -> Self {
+ Ok
+ }
+}
+
+impl Default for Ok {
+ fn default() -> Self {
+ Ok
+ }
+}
+
+pub struct Params;
+
+impl Params {
+ pub fn new(_: u32) -> Self {
+ Params
+ }
+}
+
+pub struct GenericsOk<T> {
+ bar: T,
+}
+
+impl<U> Default for GenericsOk<U> {
+ fn default() -> Self {
+ unimplemented!();
+ }
+}
+
+impl<'c, V> GenericsOk<V> {
+ pub fn new() -> GenericsOk<V> {
+ unimplemented!()
+ }
+}
+
+pub struct LtOk<'a> {
+ foo: &'a bool,
+}
+
+impl<'b> Default for LtOk<'b> {
+ fn default() -> Self {
+ unimplemented!();
+ }
+}
+
+impl<'c> LtOk<'c> {
+ pub fn new() -> LtOk<'c> {
+ unimplemented!()
+ }
+}
+
+pub struct LtKo<'a> {
+ foo: &'a bool,
+}
+
+impl<'c> LtKo<'c> {
+ pub fn new() -> LtKo<'c> {
+ unimplemented!()
+ }
+ // FIXME: that suggestion is missing lifetimes
+}
+
+struct Private;
+
+impl Private {
+ fn new() -> Private {
+ unimplemented!()
+ } // We don't lint private items
+}
+
+struct PrivateStruct;
+
+impl PrivateStruct {
+ pub fn new() -> PrivateStruct {
+ unimplemented!()
+ } // We don't lint public items on private structs
+}
+
+pub struct PrivateItem;
+
+impl PrivateItem {
+ fn new() -> PrivateItem {
+ unimplemented!()
+ } // We don't lint private items on public structs
+}
+
+struct Const;
+
+impl Const {
+ pub const fn new() -> Const {
+ Const
+ } // const fns can't be implemented via Default
+}
+
+pub struct IgnoreGenericNew;
+
+impl IgnoreGenericNew {
+ pub fn new<T>() -> Self {
+ IgnoreGenericNew
+ } // the derived Default does not make sense here as the result depends on T
+}
+
+pub trait TraitWithNew: Sized {
+ fn new() -> Self {
+ panic!()
+ }
+}
+
+pub struct IgnoreUnsafeNew;
+
+impl IgnoreUnsafeNew {
+ pub unsafe fn new() -> Self {
+ IgnoreUnsafeNew
+ }
+}
+
+#[derive(Default)]
+pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
+
+impl<'a, T> OptionRefWrapper<'a, T> {
+ pub fn new() -> Self {
+ OptionRefWrapper(None)
+ }
+}
+
+pub struct Allow(Foo);
+
+impl Allow {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ unimplemented!()
+ }
+}
+
+pub struct AllowDerive;
+
+impl AllowDerive {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ unimplemented!()
+ }
+}
+
+pub struct NewNotEqualToDerive {
+ foo: i32,
+}
+
+impl NewNotEqualToDerive {
+ // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving.
+ pub fn new() -> Self {
+ NewNotEqualToDerive { foo: 1 }
+ }
+}
+
+// see #6933
+pub struct FooGenerics<T>(std::marker::PhantomData<T>);
+impl<T> FooGenerics<T> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+}
+
+pub struct BarGenerics<T>(std::marker::PhantomData<T>);
+impl<T: Copy> BarGenerics<T> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+}
+
+pub mod issue7220 {
+ pub struct Foo<T> {
+ _bar: *mut T,
+ }
+
+ impl<T> Foo<T> {
+ pub fn new() -> Self {
+ todo!()
+ }
+ }
+}
+
+// see issue #8152
+// This should not create any lints
+pub struct DocHidden;
+impl DocHidden {
+ #[doc(hidden)]
+ pub fn new() -> Self {
+ DocHidden
+ }
+}
+
+fn main() {}
+
+pub struct IgnoreConstGenericNew(usize);
+impl IgnoreConstGenericNew {
+ pub fn new<const N: usize>() -> Self {
+ Self(N)
+ }
+}
+
+pub struct IgnoreLifetimeNew;
+impl IgnoreLifetimeNew {
+ pub fn new<'a>() -> Self {
+ Self
+ }
+}
diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr
new file mode 100644
index 000000000..212a69ab9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/new_without_default.stderr
@@ -0,0 +1,124 @@
+error: you should consider adding a `Default` implementation for `Foo`
+ --> $DIR/new_without_default.rs:7:5
+ |
+LL | / pub fn new() -> Foo {
+LL | | Foo
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::new-without-default` implied by `-D warnings`
+help: try adding this
+ |
+LL + impl Default for Foo {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `Bar`
+ --> $DIR/new_without_default.rs:15:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Bar
+LL | | }
+ | |_____^
+ |
+help: try adding this
+ |
+LL + impl Default for Bar {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `LtKo<'c>`
+ --> $DIR/new_without_default.rs:79:5
+ |
+LL | / pub fn new() -> LtKo<'c> {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+help: try adding this
+ |
+LL + impl<'c> Default for LtKo<'c> {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
+ --> $DIR/new_without_default.rs:172:5
+ |
+LL | / pub fn new() -> Self {
+LL | | NewNotEqualToDerive { foo: 1 }
+LL | | }
+ | |_____^
+ |
+help: try adding this
+ |
+LL + impl Default for NewNotEqualToDerive {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `FooGenerics<T>`
+ --> $DIR/new_without_default.rs:180:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Self(Default::default())
+LL | | }
+ | |_____^
+ |
+help: try adding this
+ |
+LL + impl<T> Default for FooGenerics<T> {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `BarGenerics<T>`
+ --> $DIR/new_without_default.rs:187:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Self(Default::default())
+LL | | }
+ | |_____^
+ |
+help: try adding this
+ |
+LL + impl<T: Copy> Default for BarGenerics<T> {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+ |
+
+error: you should consider adding a `Default` implementation for `Foo<T>`
+ --> $DIR/new_without_default.rs:198:9
+ |
+LL | / pub fn new() -> Self {
+LL | | todo!()
+LL | | }
+ | |_________^
+ |
+help: try adding this
+ |
+LL ~ impl<T> Default for Foo<T> {
+LL + fn default() -> Self {
+LL + Self::new()
+LL + }
+LL + }
+LL +
+LL ~ impl<T> Foo<T> {
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
new file mode 100644
index 000000000..fdefb11ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -0,0 +1,143 @@
+#![feature(box_syntax, fn_traits, unboxed_closures)]
+#![warn(clippy::no_effect_underscore_binding)]
+#![allow(dead_code)]
+#![allow(path_statements)]
+#![allow(clippy::deref_addrof)]
+#![allow(clippy::redundant_field_names)]
+
+struct Unit;
+struct Tuple(i32);
+struct Struct {
+ field: i32,
+}
+enum Enum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+struct DropUnit;
+impl Drop for DropUnit {
+ fn drop(&mut self) {}
+}
+struct DropStruct {
+ field: i32,
+}
+impl Drop for DropStruct {
+ fn drop(&mut self) {}
+}
+struct DropTuple(i32);
+impl Drop for DropTuple {
+ fn drop(&mut self) {}
+}
+enum DropEnum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+impl Drop for DropEnum {
+ fn drop(&mut self) {}
+}
+struct FooString {
+ s: String,
+}
+union Union {
+ a: u8,
+ b: f64,
+}
+
+fn get_number() -> i32 {
+ 0
+}
+fn get_struct() -> Struct {
+ Struct { field: 0 }
+}
+fn get_drop_struct() -> DropStruct {
+ DropStruct { field: 0 }
+}
+
+unsafe fn unsafe_fn() -> i32 {
+ 0
+}
+
+struct GreetStruct1;
+
+impl FnOnce<(&str,)> for GreetStruct1 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
+struct GreetStruct2();
+
+impl FnOnce<(&str,)> for GreetStruct2 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
+struct GreetStruct3;
+
+impl FnOnce<(&str,)> for GreetStruct3 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
+fn main() {
+ let s = get_struct();
+ let s2 = get_struct();
+
+ 0;
+ s2;
+ Unit;
+ Tuple(0);
+ Struct { field: 0 };
+ Struct { ..s };
+ Union { a: 0 };
+ Enum::Tuple(0);
+ Enum::Struct { field: 0 };
+ 5 + 6;
+ *&42;
+ &6;
+ (5, 6, 7);
+ box 42;
+ ..;
+ 5..;
+ ..5;
+ 5..6;
+ 5..=6;
+ [42, 55];
+ [42, 55][1];
+ (42, 55).1;
+ [42; 55];
+ [42; 55][13];
+ let mut x = 0;
+ || x += 5;
+ let s: String = "foo".into();
+ FooString { s: s };
+ let _unused = 1;
+ let _penguin = || println!("Some helpful closure");
+ let _duck = Struct { field: 0 };
+ let _cat = [2, 4, 6, 8][2];
+
+ #[allow(clippy::no_effect)]
+ 0;
+
+ // Do not warn
+ get_number();
+ unsafe { unsafe_fn() };
+ let _used = get_struct();
+ let _x = vec![1];
+ DropUnit;
+ DropStruct { field: 0 };
+ DropTuple(0);
+ DropEnum::Tuple(0);
+ DropEnum::Struct { field: 0 };
+ GreetStruct1("world");
+ GreetStruct2()("world");
+ GreetStruct3 {}("world");
+}
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
new file mode 100644
index 000000000..328d2555c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -0,0 +1,186 @@
+error: statement with no effect
+ --> $DIR/no_effect.rs:94:5
+ |
+LL | 0;
+ | ^^
+ |
+ = note: `-D clippy::no-effect` implied by `-D warnings`
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:95:5
+ |
+LL | s2;
+ | ^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:96:5
+ |
+LL | Unit;
+ | ^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:97:5
+ |
+LL | Tuple(0);
+ | ^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:98:5
+ |
+LL | Struct { field: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:99:5
+ |
+LL | Struct { ..s };
+ | ^^^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:100:5
+ |
+LL | Union { a: 0 };
+ | ^^^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:101:5
+ |
+LL | Enum::Tuple(0);
+ | ^^^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:102:5
+ |
+LL | Enum::Struct { field: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:103:5
+ |
+LL | 5 + 6;
+ | ^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:104:5
+ |
+LL | *&42;
+ | ^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:105:5
+ |
+LL | &6;
+ | ^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:106:5
+ |
+LL | (5, 6, 7);
+ | ^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:107:5
+ |
+LL | box 42;
+ | ^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:108:5
+ |
+LL | ..;
+ | ^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:109:5
+ |
+LL | 5..;
+ | ^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:110:5
+ |
+LL | ..5;
+ | ^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:111:5
+ |
+LL | 5..6;
+ | ^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:112:5
+ |
+LL | 5..=6;
+ | ^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:113:5
+ |
+LL | [42, 55];
+ | ^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:114:5
+ |
+LL | [42, 55][1];
+ | ^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:115:5
+ |
+LL | (42, 55).1;
+ | ^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:116:5
+ |
+LL | [42; 55];
+ | ^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:117:5
+ |
+LL | [42; 55][13];
+ | ^^^^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:119:5
+ |
+LL | || x += 5;
+ | ^^^^^^^^^^
+
+error: statement with no effect
+ --> $DIR/no_effect.rs:121:5
+ |
+LL | FooString { s: s };
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: binding to `_` prefixed variable with no side-effect
+ --> $DIR/no_effect.rs:122:5
+ |
+LL | let _unused = 1;
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
+
+error: binding to `_` prefixed variable with no side-effect
+ --> $DIR/no_effect.rs:123:5
+ |
+LL | let _penguin = || println!("Some helpful closure");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: binding to `_` prefixed variable with no side-effect
+ --> $DIR/no_effect.rs:124:5
+ |
+LL | let _duck = Struct { field: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: binding to `_` prefixed variable with no side-effect
+ --> $DIR/no_effect.rs:125:5
+ |
+LL | let _cat = [2, 4, 6, 8][2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 30 previous errors
+
diff --git a/src/tools/clippy/tests/ui/no_effect_replace.rs b/src/tools/clippy/tests/ui/no_effect_replace.rs
new file mode 100644
index 000000000..ad17d53f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect_replace.rs
@@ -0,0 +1,51 @@
+#![warn(clippy::no_effect_replace)]
+
+fn main() {
+ let _ = "12345".replace('1', "1");
+ let _ = "12345".replace("12", "12");
+ let _ = String::new().replace("12", "12");
+
+ let _ = "12345".replacen('1', "1", 1);
+ let _ = "12345".replacen("12", "12", 1);
+ let _ = String::new().replacen("12", "12", 1);
+
+ let _ = "12345".replace("12", "22");
+ let _ = "12345".replacen("12", "22", 1);
+
+ let mut x = X::default();
+ let _ = "hello".replace(&x.f(), &x.f());
+ let _ = "hello".replace(&x.f(), &x.ff());
+
+ let _ = "hello".replace(&y(), &y());
+ let _ = "hello".replace(&y(), &z());
+
+ let _ = Replaceme.replace("a", "a");
+}
+
+#[derive(Default)]
+struct X {}
+
+impl X {
+ fn f(&mut self) -> String {
+ "he".to_string()
+ }
+
+ fn ff(&mut self) -> String {
+ "hh".to_string()
+ }
+}
+
+fn y() -> String {
+ "he".to_string()
+}
+
+fn z() -> String {
+ "hh".to_string()
+}
+
+struct Replaceme;
+impl Replaceme {
+ pub fn replace(&mut self, a: &str, b: &str) -> Self {
+ Self
+ }
+}
diff --git a/src/tools/clippy/tests/ui/no_effect_replace.stderr b/src/tools/clippy/tests/ui/no_effect_replace.stderr
new file mode 100644
index 000000000..53a28aa73
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_effect_replace.stderr
@@ -0,0 +1,52 @@
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:4:13
+ |
+LL | let _ = "12345".replace('1', "1");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::no-effect-replace` implied by `-D warnings`
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:5:13
+ |
+LL | let _ = "12345".replace("12", "12");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:6:13
+ |
+LL | let _ = String::new().replace("12", "12");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:8:13
+ |
+LL | let _ = "12345".replacen('1', "1", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:9:13
+ |
+LL | let _ = "12345".replacen("12", "12", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:10:13
+ |
+LL | let _ = String::new().replacen("12", "12", 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:16:13
+ |
+LL | let _ = "hello".replace(&x.f(), &x.f());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: replacing text with itself
+ --> $DIR/no_effect_replace.rs:19:13
+ |
+LL | let _ = "hello".replace(&y(), &y());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs
new file mode 100644
index 000000000..583096ac0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_expressive_names.rs
@@ -0,0 +1,58 @@
+#![warn(clippy::all)]
+#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)]
+
+#[derive(Clone, Debug)]
+enum MaybeInst {
+ Split,
+ Split1(usize),
+ Split2(usize),
+}
+
+struct InstSplit {
+ uiae: usize,
+}
+
+impl MaybeInst {
+ fn fill(&mut self) {
+ #[allow(non_fmt_panics)]
+ let filled = match *self {
+ MaybeInst::Split1(goto1) => panic!("1"),
+ MaybeInst::Split2(goto2) => panic!("2"),
+ _ => unimplemented!(),
+ };
+ unimplemented!()
+ }
+}
+
+fn underscores_and_numbers() {
+ let _1 = 1; //~ERROR Consider a more descriptive name
+ let ____1 = 1; //~ERROR Consider a more descriptive name
+ let __1___2 = 12; //~ERROR Consider a more descriptive name
+ let _1_ok = 1;
+}
+
+fn issue2927() {
+ let args = 1;
+ format!("{:?}", 2);
+}
+
+fn issue3078() {
+ #[allow(clippy::single_match)]
+ match "a" {
+ stringify!(a) => {},
+ _ => {},
+ }
+}
+
+struct Bar;
+
+impl Bar {
+ fn bar() {
+ let _1 = 1;
+ let ____1 = 1;
+ let __1___2 = 12;
+ let _1_ok = 1;
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr
new file mode 100644
index 000000000..116d5da87
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr
@@ -0,0 +1,40 @@
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:28:9
+ |
+LL | let _1 = 1; //~ERROR Consider a more descriptive name
+ | ^^
+ |
+ = note: `-D clippy::just-underscores-and-digits` implied by `-D warnings`
+
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:29:9
+ |
+LL | let ____1 = 1; //~ERROR Consider a more descriptive name
+ | ^^^^^
+
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:30:9
+ |
+LL | let __1___2 = 12; //~ERROR Consider a more descriptive name
+ | ^^^^^^^
+
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:51:13
+ |
+LL | let _1 = 1;
+ | ^^
+
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:52:13
+ |
+LL | let ____1 = 1;
+ | ^^^^^
+
+error: consider choosing a more descriptive name
+ --> $DIR/non_expressive_names.rs:53:13
+ |
+LL | let __1___2 = 12;
+ | ^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
new file mode 100644
index 000000000..a9b2dcfb0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
@@ -0,0 +1,33 @@
+// ignore-windows
+// run-rustfix
+#![warn(clippy::non_octal_unix_permissions)]
+use std::fs::{DirBuilder, File, OpenOptions, Permissions};
+use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt};
+
+fn main() {
+ let permissions = 0o760;
+
+ // OpenOptionsExt::mode
+ let mut options = OpenOptions::new();
+ options.mode(0o440);
+ options.mode(0o400);
+ options.mode(permissions);
+
+ // PermissionsExt::from_mode
+ let _permissions = Permissions::from_mode(0o647);
+ let _permissions = Permissions::from_mode(0o000);
+ let _permissions = Permissions::from_mode(permissions);
+
+ // PermissionsExt::set_mode
+ let f = File::create("foo.txt").unwrap();
+ let metadata = f.metadata().unwrap();
+ let mut permissions = metadata.permissions();
+
+ permissions.set_mode(0o644);
+ permissions.set_mode(0o704);
+
+ // DirBuilderExt::mode
+ let mut builder = DirBuilder::new();
+ builder.mode(0o755);
+ builder.mode(0o406);
+}
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
new file mode 100644
index 000000000..7d2922f49
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
@@ -0,0 +1,33 @@
+// ignore-windows
+// run-rustfix
+#![warn(clippy::non_octal_unix_permissions)]
+use std::fs::{DirBuilder, File, OpenOptions, Permissions};
+use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt};
+
+fn main() {
+ let permissions = 0o760;
+
+ // OpenOptionsExt::mode
+ let mut options = OpenOptions::new();
+ options.mode(440);
+ options.mode(0o400);
+ options.mode(permissions);
+
+ // PermissionsExt::from_mode
+ let _permissions = Permissions::from_mode(647);
+ let _permissions = Permissions::from_mode(0o000);
+ let _permissions = Permissions::from_mode(permissions);
+
+ // PermissionsExt::set_mode
+ let f = File::create("foo.txt").unwrap();
+ let metadata = f.metadata().unwrap();
+ let mut permissions = metadata.permissions();
+
+ permissions.set_mode(644);
+ permissions.set_mode(0o704);
+
+ // DirBuilderExt::mode
+ let mut builder = DirBuilder::new();
+ builder.mode(755);
+ builder.mode(0o406);
+}
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr
new file mode 100644
index 000000000..32845d065
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr
@@ -0,0 +1,28 @@
+error: using a non-octal value to set unix file permissions
+ --> $DIR/non_octal_unix_permissions.rs:12:18
+ |
+LL | options.mode(440);
+ | ^^^ help: consider using an octal literal instead: `0o440`
+ |
+ = note: `-D clippy::non-octal-unix-permissions` implied by `-D warnings`
+
+error: using a non-octal value to set unix file permissions
+ --> $DIR/non_octal_unix_permissions.rs:17:47
+ |
+LL | let _permissions = Permissions::from_mode(647);
+ | ^^^ help: consider using an octal literal instead: `0o647`
+
+error: using a non-octal value to set unix file permissions
+ --> $DIR/non_octal_unix_permissions.rs:26:26
+ |
+LL | permissions.set_mode(644);
+ | ^^^ help: consider using an octal literal instead: `0o644`
+
+error: using a non-octal value to set unix file permissions
+ --> $DIR/non_octal_unix_permissions.rs:31:18
+ |
+LL | builder.mode(755);
+ | ^^^ help: consider using an octal literal instead: `0o755`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
new file mode 100644
index 000000000..514fb25c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
@@ -0,0 +1,133 @@
+#![warn(clippy::non_send_fields_in_send_ty)]
+#![allow(suspicious_auto_trait_impls)]
+#![feature(extern_types)]
+
+use std::cell::UnsafeCell;
+use std::ptr::NonNull;
+use std::rc::Rc;
+use std::sync::{Arc, Mutex, MutexGuard};
+
+// disrustor / RUSTSEC-2020-0150
+pub struct RingBuffer<T> {
+ data: Vec<UnsafeCell<T>>,
+ capacity: usize,
+ mask: usize,
+}
+
+unsafe impl<T> Send for RingBuffer<T> {}
+
+// noise_search / RUSTSEC-2020-0141
+pub struct MvccRwLock<T> {
+ raw: *const T,
+ lock: Mutex<Box<T>>,
+}
+
+unsafe impl<T> Send for MvccRwLock<T> {}
+
+// async-coap / RUSTSEC-2020-0124
+pub struct ArcGuard<RC, T> {
+ inner: T,
+ head: Arc<RC>,
+}
+
+unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
+
+// rusb / RUSTSEC-2020-0098
+extern "C" {
+ type libusb_device_handle;
+}
+
+pub trait UsbContext {
+ // some user trait that does not guarantee `Send`
+}
+
+pub struct DeviceHandle<T: UsbContext> {
+ context: T,
+ handle: NonNull<libusb_device_handle>,
+}
+
+unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
+
+// Other basic tests
+pub struct NoGeneric {
+ rc_is_not_send: Rc<String>,
+}
+
+unsafe impl Send for NoGeneric {}
+
+pub struct MultiField<T> {
+ field1: T,
+ field2: T,
+ field3: T,
+}
+
+unsafe impl<T> Send for MultiField<T> {}
+
+pub enum MyOption<T> {
+ MySome(T),
+ MyNone,
+}
+
+unsafe impl<T> Send for MyOption<T> {}
+
+// Test types that contain `NonNull` instead of raw pointers (#8045)
+pub struct WrappedNonNull(UnsafeCell<NonNull<()>>);
+
+unsafe impl Send for WrappedNonNull {}
+
+// Multiple type parameters
+pub struct MultiParam<A, B> {
+ vec: Vec<(A, B)>,
+}
+
+unsafe impl<A, B> Send for MultiParam<A, B> {}
+
+// Tests for raw pointer heuristic
+extern "C" {
+ type NonSend;
+}
+
+pub struct HeuristicTest {
+ // raw pointers are allowed
+ field1: Vec<*const NonSend>,
+ field2: [*const NonSend; 3],
+ field3: (*const NonSend, *const NonSend, *const NonSend),
+ // not allowed when it contains concrete `!Send` field
+ field4: (*const NonSend, Rc<u8>),
+ // nested raw pointer is also allowed
+ field5: Vec<Vec<*const NonSend>>,
+}
+
+unsafe impl Send for HeuristicTest {}
+
+// Test attributes
+#[allow(clippy::non_send_fields_in_send_ty)]
+pub struct AttrTest1<T>(T);
+
+pub struct AttrTest2<T> {
+ #[allow(clippy::non_send_fields_in_send_ty)]
+ field: T,
+}
+
+pub enum AttrTest3<T> {
+ #[allow(clippy::non_send_fields_in_send_ty)]
+ Enum1(T),
+ Enum2(T),
+}
+
+unsafe impl<T> Send for AttrTest1<T> {}
+unsafe impl<T> Send for AttrTest2<T> {}
+unsafe impl<T> Send for AttrTest3<T> {}
+
+// Multiple non-overlapping `Send` for a single type
+pub struct Complex<A, B> {
+ field1: A,
+ field2: B,
+}
+
+unsafe impl<P> Send for Complex<P, u32> {}
+
+// `MutexGuard` is non-Send
+unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
new file mode 100644
index 000000000..b6c904a14
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
@@ -0,0 +1,171 @@
+error: some fields in `RingBuffer<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:17:1
+ |
+LL | unsafe impl<T> Send for RingBuffer<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
+note: it is not safe to send field `data` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:12:5
+ |
+LL | data: Vec<UnsafeCell<T>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: add bounds on type parameter `T` that satisfy `Vec<UnsafeCell<T>>: Send`
+
+error: some fields in `MvccRwLock<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:25:1
+ |
+LL | unsafe impl<T> Send for MvccRwLock<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `lock` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:22:5
+ |
+LL | lock: Mutex<Box<T>>,
+ | ^^^^^^^^^^^^^^^^^^^
+ = help: add bounds on type parameter `T` that satisfy `Mutex<Box<T>>: Send`
+
+error: some fields in `ArcGuard<RC, T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:33:1
+ |
+LL | unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `head` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:30:5
+ |
+LL | head: Arc<RC>,
+ | ^^^^^^^^^^^^^
+ = help: add bounds on type parameter `RC` that satisfy `Arc<RC>: Send`
+
+error: some fields in `DeviceHandle<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:49:1
+ |
+LL | unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `context` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:45:5
+ |
+LL | context: T,
+ | ^^^^^^^^^^
+ = help: add `T: Send` bound in `Send` impl
+
+error: some fields in `NoGeneric` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:56:1
+ |
+LL | unsafe impl Send for NoGeneric {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `rc_is_not_send` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:53:5
+ |
+LL | rc_is_not_send: Rc<String>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+
+error: some fields in `MultiField<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:64:1
+ |
+LL | unsafe impl<T> Send for MultiField<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field1` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:59:5
+ |
+LL | field1: T,
+ | ^^^^^^^^^
+ = help: add `T: Send` bound in `Send` impl
+note: it is not safe to send field `field2` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:60:5
+ |
+LL | field2: T,
+ | ^^^^^^^^^
+ = help: add `T: Send` bound in `Send` impl
+note: it is not safe to send field `field3` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:61:5
+ |
+LL | field3: T,
+ | ^^^^^^^^^
+ = help: add `T: Send` bound in `Send` impl
+
+error: some fields in `MyOption<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:71:1
+ |
+LL | unsafe impl<T> Send for MyOption<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `0` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:67:12
+ |
+LL | MySome(T),
+ | ^
+ = help: add `T: Send` bound in `Send` impl
+
+error: some fields in `MultiParam<A, B>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:83:1
+ |
+LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `vec` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:80:5
+ |
+LL | vec: Vec<(A, B)>,
+ | ^^^^^^^^^^^^^^^^
+ = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
+
+error: some fields in `HeuristicTest` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:101:1
+ |
+LL | unsafe impl Send for HeuristicTest {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field4` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:96:5
+ |
+LL | field4: (*const NonSend, Rc<u8>),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+
+error: some fields in `AttrTest3<T>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:120:1
+ |
+LL | unsafe impl<T> Send for AttrTest3<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `0` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:115:11
+ |
+LL | Enum2(T),
+ | ^
+ = help: add `T: Send` bound in `Send` impl
+
+error: some fields in `Complex<P, u32>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:128:1
+ |
+LL | unsafe impl<P> Send for Complex<P, u32> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field1` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:124:5
+ |
+LL | field1: A,
+ | ^^^^^^^^^
+ = help: add `P: Send` bound in `Send` impl
+
+error: some fields in `Complex<Q, MutexGuard<'static, bool>>` are not safe to be sent to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:131:1
+ |
+LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field2` to another thread
+ --> $DIR/non_send_fields_in_send_ty.rs:125:5
+ |
+LL | field2: B,
+ | ^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
new file mode 100644
index 000000000..24ae62bb0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -0,0 +1,59 @@
+#![feature(lint_reasons)]
+#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::nonminimal_bool)]
+
+fn main() {
+ let a: bool = unimplemented!();
+ let b: bool = unimplemented!();
+ let c: bool = unimplemented!();
+ let d: bool = unimplemented!();
+ let e: bool = unimplemented!();
+ let _ = !true;
+ let _ = !false;
+ let _ = !!a;
+ let _ = false || a;
+ // don't lint on cfgs
+ let _ = cfg!(you_shall_not_not_pass) && a;
+ let _ = a || !b || !c || !d || !e;
+ let _ = !(!a && b);
+ let _ = !(!a || b);
+ let _ = !a && !(b && c);
+}
+
+fn equality_stuff() {
+ let a: i32 = unimplemented!();
+ let b: i32 = unimplemented!();
+ let c: i32 = unimplemented!();
+ let d: i32 = unimplemented!();
+ let _ = a == b && c == 5 && a == b;
+ let _ = a == b || c == 5 || a == b;
+ let _ = a == b && c == 5 && b == a;
+ let _ = a != b || !(a != b || c == d);
+ let _ = a != b && !(a != b && c == d);
+}
+
+fn issue3847(a: u32, b: u32) -> bool {
+ const THRESHOLD: u32 = 1_000;
+
+ if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD {
+ return false;
+ }
+ true
+}
+
+fn issue4548() {
+ fn f(_i: u32, _j: u32) -> u32 {
+ unimplemented!();
+ }
+
+ let i = 0;
+ let j = 0;
+
+ if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
+}
+
+fn check_expect() {
+ let a: bool = unimplemented!();
+ #[expect(clippy::nonminimal_bool)]
+ let _ = !!a;
+}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
new file mode 100644
index 000000000..fc6a5ce1d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -0,0 +1,111 @@
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:11:13
+ |
+LL | let _ = !true;
+ | ^^^^^ help: try: `false`
+ |
+ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:12:13
+ |
+LL | let _ = !false;
+ | ^^^^^^ help: try: `true`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:13:13
+ |
+LL | let _ = !!a;
+ | ^^^ help: try: `a`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:14:13
+ |
+LL | let _ = false || a;
+ | ^^^^^^^^^^ help: try: `a`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:18:13
+ |
+LL | let _ = !(!a && b);
+ | ^^^^^^^^^^ help: try: `a || !b`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:19:13
+ |
+LL | let _ = !(!a || b);
+ | ^^^^^^^^^^ help: try: `a && !b`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:20:13
+ |
+LL | let _ = !a && !(b && c);
+ | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:28:13
+ |
+LL | let _ = a == b && c == 5 && a == b;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | let _ = !(a != b || c != 5);
+ | ~~~~~~~~~~~~~~~~~~~
+LL | let _ = a == b && c == 5;
+ | ~~~~~~~~~~~~~~~~
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:29:13
+ |
+LL | let _ = a == b || c == 5 || a == b;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | let _ = !(a != b && c != 5);
+ | ~~~~~~~~~~~~~~~~~~~
+LL | let _ = a == b || c == 5;
+ | ~~~~~~~~~~~~~~~~
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:30:13
+ |
+LL | let _ = a == b && c == 5 && b == a;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | let _ = !(a != b || c != 5);
+ | ~~~~~~~~~~~~~~~~~~~
+LL | let _ = a == b && c == 5;
+ | ~~~~~~~~~~~~~~~~
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:31:13
+ |
+LL | let _ = a != b || !(a != b || c == d);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | let _ = !(a == b && c == d);
+ | ~~~~~~~~~~~~~~~~~~~
+LL | let _ = a != b || c != d;
+ | ~~~~~~~~~~~~~~~~
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool.rs:32:13
+ |
+LL | let _ = a != b && !(a != b && c == d);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | let _ = !(a == b || c == d);
+ | ~~~~~~~~~~~~~~~~~~~
+LL | let _ = a != b && c != d;
+ | ~~~~~~~~~~~~~~~~
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
new file mode 100644
index 000000000..aad44089d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
@@ -0,0 +1,111 @@
+// run-rustfix
+#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::nonminimal_bool)]
+
+fn methods_with_negation() {
+ let a: Option<i32> = unimplemented!();
+ let b: Result<i32, i32> = unimplemented!();
+ let _ = a.is_some();
+ let _ = a.is_none();
+ let _ = a.is_none();
+ let _ = a.is_some();
+ let _ = b.is_err();
+ let _ = b.is_ok();
+ let _ = b.is_ok();
+ let _ = b.is_err();
+ let c = false;
+ let _ = a.is_none() || c;
+ let _ = a.is_none() && c;
+ let _ = !(!c ^ c) || a.is_none();
+ let _ = (!c ^ c) || a.is_none();
+ let _ = !c ^ c || a.is_none();
+}
+
+// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
+// clippy::nonminimal_bool should only check the built-in Result and Some type, not
+// any other types like the following.
+enum CustomResultOk<E> {
+ Ok,
+ Err(E),
+}
+enum CustomResultErr<E> {
+ Ok,
+ Err(E),
+}
+enum CustomSomeSome<T> {
+ Some(T),
+ None,
+}
+enum CustomSomeNone<T> {
+ Some(T),
+ None,
+}
+
+impl<E> CustomResultOk<E> {
+ pub fn is_ok(&self) -> bool {
+ true
+ }
+}
+
+impl<E> CustomResultErr<E> {
+ pub fn is_err(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeSome<T> {
+ pub fn is_some(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeNone<T> {
+ pub fn is_none(&self) -> bool {
+ true
+ }
+}
+
+fn dont_warn_for_custom_methods_with_negation() {
+ let res = CustomResultOk::Err("Error");
+ // Should not warn and suggest 'is_err()' because the type does not
+ // implement is_err().
+ if !res.is_ok() {}
+
+ let res = CustomResultErr::Err("Error");
+ // Should not warn and suggest 'is_ok()' because the type does not
+ // implement is_ok().
+ if !res.is_err() {}
+
+ let res = CustomSomeSome::Some("thing");
+ // Should not warn and suggest 'is_none()' because the type does not
+ // implement is_none().
+ if !res.is_some() {}
+
+ let res = CustomSomeNone::Some("thing");
+ // Should not warn and suggest 'is_some()' because the type does not
+ // implement is_some().
+ if !res.is_none() {}
+}
+
+// Only Built-in Result and Some types should suggest the negated alternative
+fn warn_for_built_in_methods_with_negation() {
+ let res: Result<usize, usize> = Ok(1);
+ if res.is_err() {}
+ if res.is_ok() {}
+
+ let res = Some(1);
+ if res.is_none() {}
+ if res.is_some() {}
+}
+
+#[allow(clippy::neg_cmp_op_on_partial_ord)]
+fn dont_warn_for_negated_partial_ord_comparison() {
+ let a: f64 = unimplemented!();
+ let b: f64 = unimplemented!();
+ let _ = !(a < b);
+ let _ = !(a <= b);
+ let _ = !(a > b);
+ let _ = !(a >= b);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
new file mode 100644
index 000000000..b9074da84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
@@ -0,0 +1,111 @@
+// run-rustfix
+#![allow(unused, clippy::diverging_sub_expression)]
+#![warn(clippy::nonminimal_bool)]
+
+fn methods_with_negation() {
+ let a: Option<i32> = unimplemented!();
+ let b: Result<i32, i32> = unimplemented!();
+ let _ = a.is_some();
+ let _ = !a.is_some();
+ let _ = a.is_none();
+ let _ = !a.is_none();
+ let _ = b.is_err();
+ let _ = !b.is_err();
+ let _ = b.is_ok();
+ let _ = !b.is_ok();
+ let c = false;
+ let _ = !(a.is_some() && !c);
+ let _ = !(a.is_some() || !c);
+ let _ = !(!c ^ c) || !a.is_some();
+ let _ = (!c ^ c) || !a.is_some();
+ let _ = !c ^ c || !a.is_some();
+}
+
+// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
+// clippy::nonminimal_bool should only check the built-in Result and Some type, not
+// any other types like the following.
+enum CustomResultOk<E> {
+ Ok,
+ Err(E),
+}
+enum CustomResultErr<E> {
+ Ok,
+ Err(E),
+}
+enum CustomSomeSome<T> {
+ Some(T),
+ None,
+}
+enum CustomSomeNone<T> {
+ Some(T),
+ None,
+}
+
+impl<E> CustomResultOk<E> {
+ pub fn is_ok(&self) -> bool {
+ true
+ }
+}
+
+impl<E> CustomResultErr<E> {
+ pub fn is_err(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeSome<T> {
+ pub fn is_some(&self) -> bool {
+ true
+ }
+}
+
+impl<T> CustomSomeNone<T> {
+ pub fn is_none(&self) -> bool {
+ true
+ }
+}
+
+fn dont_warn_for_custom_methods_with_negation() {
+ let res = CustomResultOk::Err("Error");
+ // Should not warn and suggest 'is_err()' because the type does not
+ // implement is_err().
+ if !res.is_ok() {}
+
+ let res = CustomResultErr::Err("Error");
+ // Should not warn and suggest 'is_ok()' because the type does not
+ // implement is_ok().
+ if !res.is_err() {}
+
+ let res = CustomSomeSome::Some("thing");
+ // Should not warn and suggest 'is_none()' because the type does not
+ // implement is_none().
+ if !res.is_some() {}
+
+ let res = CustomSomeNone::Some("thing");
+ // Should not warn and suggest 'is_some()' because the type does not
+ // implement is_some().
+ if !res.is_none() {}
+}
+
+// Only Built-in Result and Some types should suggest the negated alternative
+fn warn_for_built_in_methods_with_negation() {
+ let res: Result<usize, usize> = Ok(1);
+ if !res.is_ok() {}
+ if !res.is_err() {}
+
+ let res = Some(1);
+ if !res.is_some() {}
+ if !res.is_none() {}
+}
+
+#[allow(clippy::neg_cmp_op_on_partial_ord)]
+fn dont_warn_for_negated_partial_ord_comparison() {
+ let a: f64 = unimplemented!();
+ let b: f64 = unimplemented!();
+ let _ = !(a < b);
+ let _ = !(a <= b);
+ let _ = !(a > b);
+ let _ = !(a >= b);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
new file mode 100644
index 000000000..21b84db85
--- /dev/null
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
@@ -0,0 +1,82 @@
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:9:13
+ |
+LL | let _ = !a.is_some();
+ | ^^^^^^^^^^^^ help: try: `a.is_none()`
+ |
+ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:11:13
+ |
+LL | let _ = !a.is_none();
+ | ^^^^^^^^^^^^ help: try: `a.is_some()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:13:13
+ |
+LL | let _ = !b.is_err();
+ | ^^^^^^^^^^^ help: try: `b.is_ok()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:15:13
+ |
+LL | let _ = !b.is_ok();
+ | ^^^^^^^^^^ help: try: `b.is_err()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:17:13
+ |
+LL | let _ = !(a.is_some() && !c);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:18:13
+ |
+LL | let _ = !(a.is_some() || !c);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:19:26
+ |
+LL | let _ = !(!c ^ c) || !a.is_some();
+ | ^^^^^^^^^^^^ help: try: `a.is_none()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:20:25
+ |
+LL | let _ = (!c ^ c) || !a.is_some();
+ | ^^^^^^^^^^^^ help: try: `a.is_none()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:21:23
+ |
+LL | let _ = !c ^ c || !a.is_some();
+ | ^^^^^^^^^^^^ help: try: `a.is_none()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:93:8
+ |
+LL | if !res.is_ok() {}
+ | ^^^^^^^^^^^^ help: try: `res.is_err()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:94:8
+ |
+LL | if !res.is_err() {}
+ | ^^^^^^^^^^^^^ help: try: `res.is_ok()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:97:8
+ |
+LL | if !res.is_some() {}
+ | ^^^^^^^^^^^^^^ help: try: `res.is_none()`
+
+error: this boolean expression can be simplified
+ --> $DIR/nonminimal_bool_methods.rs:98:8
+ |
+LL | if !res.is_none() {}
+ | ^^^^^^^^^^^^^^ help: try: `res.is_some()`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/numbered_fields.fixed b/src/tools/clippy/tests/ui/numbered_fields.fixed
new file mode 100644
index 000000000..68c987eb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/numbered_fields.fixed
@@ -0,0 +1,39 @@
+//run-rustfix
+#![warn(clippy::init_numbered_fields)]
+#![allow(unused_tuple_struct_fields)]
+
+#[derive(Default)]
+struct TupleStruct(u32, u32, u8);
+
+// This shouldn't lint because it's in a macro
+macro_rules! tuple_struct_init {
+ () => {
+ TupleStruct { 0: 0, 1: 1, 2: 2 }
+ };
+}
+
+fn main() {
+ let tuple_struct = TupleStruct::default();
+
+ // This should lint
+ let _ = TupleStruct(1u32, 42, 23u8);
+
+ // This should also lint and order the fields correctly
+ let _ = TupleStruct(1u32, 3u32, 2u8);
+
+ // Ok because of default initializer
+ let _ = TupleStruct { 0: 42, ..tuple_struct };
+
+ let _ = TupleStruct {
+ 1: 23,
+ ..TupleStruct::default()
+ };
+
+ // Ok because it's in macro
+ let _ = tuple_struct_init!();
+
+ type Alias = TupleStruct;
+
+ // Aliases can't be tuple constructed #8638
+ let _ = Alias { 0: 0, 1: 1, 2: 2 };
+}
diff --git a/src/tools/clippy/tests/ui/numbered_fields.rs b/src/tools/clippy/tests/ui/numbered_fields.rs
new file mode 100644
index 000000000..2ef4fb4de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/numbered_fields.rs
@@ -0,0 +1,47 @@
+//run-rustfix
+#![warn(clippy::init_numbered_fields)]
+#![allow(unused_tuple_struct_fields)]
+
+#[derive(Default)]
+struct TupleStruct(u32, u32, u8);
+
+// This shouldn't lint because it's in a macro
+macro_rules! tuple_struct_init {
+ () => {
+ TupleStruct { 0: 0, 1: 1, 2: 2 }
+ };
+}
+
+fn main() {
+ let tuple_struct = TupleStruct::default();
+
+ // This should lint
+ let _ = TupleStruct {
+ 0: 1u32,
+ 1: 42,
+ 2: 23u8,
+ };
+
+ // This should also lint and order the fields correctly
+ let _ = TupleStruct {
+ 0: 1u32,
+ 2: 2u8,
+ 1: 3u32,
+ };
+
+ // Ok because of default initializer
+ let _ = TupleStruct { 0: 42, ..tuple_struct };
+
+ let _ = TupleStruct {
+ 1: 23,
+ ..TupleStruct::default()
+ };
+
+ // Ok because it's in macro
+ let _ = tuple_struct_init!();
+
+ type Alias = TupleStruct;
+
+ // Aliases can't be tuple constructed #8638
+ let _ = Alias { 0: 0, 1: 1, 2: 2 };
+}
diff --git a/src/tools/clippy/tests/ui/numbered_fields.stderr b/src/tools/clippy/tests/ui/numbered_fields.stderr
new file mode 100644
index 000000000..60c0d7898
--- /dev/null
+++ b/src/tools/clippy/tests/ui/numbered_fields.stderr
@@ -0,0 +1,26 @@
+error: used a field initializer for a tuple struct
+ --> $DIR/numbered_fields.rs:19:13
+ |
+LL | let _ = TupleStruct {
+ | _____________^
+LL | | 0: 1u32,
+LL | | 1: 42,
+LL | | 2: 23u8,
+LL | | };
+ | |_____^ help: try this instead: `TupleStruct(1u32, 42, 23u8)`
+ |
+ = note: `-D clippy::init-numbered-fields` implied by `-D warnings`
+
+error: used a field initializer for a tuple struct
+ --> $DIR/numbered_fields.rs:26:13
+ |
+LL | let _ = TupleStruct {
+ | _____________^
+LL | | 0: 1u32,
+LL | | 2: 2u8,
+LL | | 1: 3u32,
+LL | | };
+ | |_____^ help: try this instead: `TupleStruct(1u32, 3u32, 2u8)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed
new file mode 100644
index 000000000..62d932c2c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed
@@ -0,0 +1,7 @@
+// run-rustfix
+
+#![warn(clippy::obfuscated_if_else)]
+
+fn main() {
+ if true { "a" } else { "b" };
+}
diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.rs b/src/tools/clippy/tests/ui/obfuscated_if_else.rs
new file mode 100644
index 000000000..273be9092
--- /dev/null
+++ b/src/tools/clippy/tests/ui/obfuscated_if_else.rs
@@ -0,0 +1,7 @@
+// run-rustfix
+
+#![warn(clippy::obfuscated_if_else)]
+
+fn main() {
+ true.then_some("a").unwrap_or("b");
+}
diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr
new file mode 100644
index 000000000..e4180c288
--- /dev/null
+++ b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr
@@ -0,0 +1,10 @@
+error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..`
+ --> $DIR/obfuscated_if_else.rs:6:5
+ |
+LL | true.then_some("a").unwrap_or("b");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }`
+ |
+ = note: `-D clippy::obfuscated-if-else` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/octal_escapes.rs b/src/tools/clippy/tests/ui/octal_escapes.rs
new file mode 100644
index 000000000..53145ef0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/octal_escapes.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::octal_escapes)]
+
+fn main() {
+ let _bad1 = "\033[0m";
+ let _bad2 = b"\033[0m";
+ let _bad3 = "\\\033[0m";
+ // maximum 3 digits (\012 is the escape)
+ let _bad4 = "\01234567";
+ let _bad5 = "\0\03";
+ let _bad6 = "Text-\055\077-MoreText";
+ let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
+ let _bad8 = "锈\01锈";
+ let _bad9 = "锈\011锈";
+
+ let _good1 = "\\033[0m";
+ let _good2 = "\0\\0";
+ let _good3 = "\0\0";
+ let _good4 = "X\0\0X";
+ let _good5 = "锈\0锈";
+}
diff --git a/src/tools/clippy/tests/ui/octal_escapes.stderr b/src/tools/clippy/tests/ui/octal_escapes.stderr
new file mode 100644
index 000000000..54f5bbb0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/octal_escapes.stderr
@@ -0,0 +1,131 @@
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:4:17
+ |
+LL | let _bad1 = "/033[0m";
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::octal-escapes` implied by `-D warnings`
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad1 = "/x1b[0m";
+ | ~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad1 = "/x0033[0m";
+ | ~~~~~~~~~~~
+
+error: octal-looking escape in byte string literal
+ --> $DIR/octal_escapes.rs:5:17
+ |
+LL | let _bad2 = b"/033[0m";
+ | ^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null byte
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad2 = b"/x1b[0m";
+ | ~~~~~~~~~~
+help: if the null byte is intended, disambiguate using
+ |
+LL | let _bad2 = b"/x0033[0m";
+ | ~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:6:17
+ |
+LL | let _bad3 = "//033[0m";
+ | ^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad3 = "//x1b[0m";
+ | ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad3 = "//x0033[0m";
+ | ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:8:17
+ |
+LL | let _bad4 = "/01234567";
+ | ^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad4 = "/x0a34567";
+ | ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad4 = "/x001234567";
+ | ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:10:17
+ |
+LL | let _bad6 = "Text-/055/077-MoreText";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad6 = "Text-/x2d/x3f-MoreText";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad6 = "Text-/x0055/x0077-MoreText";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:11:17
+ |
+LL | let _bad7 = "EvenMoreText-/01/02-ShortEscapes";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad7 = "EvenMoreText-/x01/x02-ShortEscapes";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad7 = "EvenMoreText-/x001/x002-ShortEscapes";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:12:17
+ |
+LL | let _bad8 = "锈/01锈";
+ | ^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad8 = "锈/x01锈";
+ | ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad8 = "锈/x001锈";
+ | ~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:13:17
+ |
+LL | let _bad9 = "锈/011锈";
+ | ^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad9 = "锈/x09锈";
+ | ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad9 = "锈/x0011锈";
+ | ~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ok_expect.rs b/src/tools/clippy/tests/ui/ok_expect.rs
new file mode 100644
index 000000000..ff68d38c7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ok_expect.rs
@@ -0,0 +1,27 @@
+use std::io;
+
+struct MyError(()); // doesn't implement Debug
+
+#[derive(Debug)]
+struct MyErrorWithParam<T> {
+ x: T,
+}
+
+fn main() {
+ let res: Result<i32, ()> = Ok(0);
+ let _ = res.unwrap();
+
+ res.ok().expect("disaster!");
+ // the following should not warn, since `expect` isn't implemented unless
+ // the error type implements `Debug`
+ let res2: Result<i32, MyError> = Ok(0);
+ res2.ok().expect("oh noes!");
+ let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0);
+ res3.ok().expect("whoof");
+ let res4: Result<u32, io::Error> = Ok(0);
+ res4.ok().expect("argh");
+ let res5: io::Result<u32> = Ok(0);
+ res5.ok().expect("oops");
+ let res6: Result<u32, &str> = Ok(0);
+ res6.ok().expect("meh");
+}
diff --git a/src/tools/clippy/tests/ui/ok_expect.stderr b/src/tools/clippy/tests/ui/ok_expect.stderr
new file mode 100644
index 000000000..b02b28e7f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ok_expect.stderr
@@ -0,0 +1,43 @@
+error: called `ok().expect()` on a `Result` value
+ --> $DIR/ok_expect.rs:14:5
+ |
+LL | res.ok().expect("disaster!");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::ok-expect` implied by `-D warnings`
+ = help: you can call `expect()` directly on the `Result`
+
+error: called `ok().expect()` on a `Result` value
+ --> $DIR/ok_expect.rs:20:5
+ |
+LL | res3.ok().expect("whoof");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: you can call `expect()` directly on the `Result`
+
+error: called `ok().expect()` on a `Result` value
+ --> $DIR/ok_expect.rs:22:5
+ |
+LL | res4.ok().expect("argh");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: you can call `expect()` directly on the `Result`
+
+error: called `ok().expect()` on a `Result` value
+ --> $DIR/ok_expect.rs:24:5
+ |
+LL | res5.ok().expect("oops");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: you can call `expect()` directly on the `Result`
+
+error: called `ok().expect()` on a `Result` value
+ --> $DIR/ok_expect.rs:26:5
+ |
+LL | res6.ok().expect("meh");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: you can call `expect()` directly on the `Result`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion.rs b/src/tools/clippy/tests/ui/only_used_in_recursion.rs
new file mode 100644
index 000000000..5768434f9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/only_used_in_recursion.rs
@@ -0,0 +1,122 @@
+#![warn(clippy::only_used_in_recursion)]
+
+fn simple(a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { simple(a - 1, b) }
+}
+
+fn with_calc(a: usize, b: isize) -> usize {
+ if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
+}
+
+fn tuple((a, b): (usize, usize)) -> usize {
+ if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
+}
+
+fn let_tuple(a: usize, b: usize) -> usize {
+ let (c, d) = (a, b);
+ if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
+}
+
+fn array([a, b]: [usize; 2]) -> usize {
+ if a == 0 { 1 } else { array([a - 1, b + 1]) }
+}
+
+fn index(a: usize, mut b: &[usize], c: usize) -> usize {
+ if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
+}
+
+fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
+ let c = loop {
+ b += 1;
+ c += 1;
+ if c == 10 {
+ break b;
+ }
+ };
+
+ if a == 0 { 1 } else { break_(a - 1, c, c) }
+}
+
+// this has a side effect
+fn mut_ref(a: usize, b: &mut usize) -> usize {
+ *b = 1;
+ if a == 0 { 1 } else { mut_ref(a - 1, b) }
+}
+
+fn mut_ref2(a: usize, b: &mut usize) -> usize {
+ let mut c = *b;
+ if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
+}
+
+fn not_primitive(a: usize, b: String) -> usize {
+ if a == 0 { 1 } else { not_primitive(a - 1, b) }
+}
+
+// this doesn't have a side effect,
+// but `String` is not primitive.
+fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
+ if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
+}
+
+struct A;
+
+impl A {
+ fn method(a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { A::method(a - 1, b - 1) }
+ }
+
+ fn method2(&self, a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
+ }
+}
+
+trait B {
+ fn hello(a: usize, b: usize) -> usize;
+
+ fn hello2(&self, a: usize, b: usize) -> usize;
+}
+
+impl B for A {
+ fn hello(a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
+ }
+
+ fn hello2(&self, a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ }
+}
+
+trait C {
+ fn hello(a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
+ }
+
+ fn hello2(&self, a: usize, b: usize) -> usize {
+ if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ }
+}
+
+fn ignore(a: usize, _: usize) -> usize {
+ if a == 1 { 1 } else { ignore(a - 1, 0) }
+}
+
+fn ignore2(a: usize, _b: usize) -> usize {
+ if a == 1 { 1 } else { ignore2(a - 1, _b) }
+}
+
+fn f1(a: u32) -> u32 {
+ a
+}
+
+fn f2(a: u32) -> u32 {
+ f1(a)
+}
+
+fn inner_fn(a: u32) -> u32 {
+ fn inner_fn(a: u32) -> u32 {
+ a
+ }
+ inner_fn(a)
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion.stderr b/src/tools/clippy/tests/ui/only_used_in_recursion.stderr
new file mode 100644
index 000000000..6fe9361bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/only_used_in_recursion.stderr
@@ -0,0 +1,82 @@
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:3:21
+ |
+LL | fn simple(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+ |
+ = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:7:24
+ |
+LL | fn with_calc(a: usize, b: isize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:11:14
+ |
+LL | fn tuple((a, b): (usize, usize)) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:15:24
+ |
+LL | fn let_tuple(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:20:14
+ |
+LL | fn array([a, b]: [usize; 2]) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:24:20
+ |
+LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
+ | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:24:37
+ |
+LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_c`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:28:21
+ |
+LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
+ | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:46:23
+ |
+LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:51:28
+ |
+LL | fn not_primitive(a: usize, b: String) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:68:33
+ |
+LL | fn method2(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:90:24
+ |
+LL | fn hello(a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:94:32
+ |
+LL | fn hello2(&self, a: usize, b: usize) -> usize {
+ | ^ help: if this is intentional, prefix with an underscore: `_b`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs
new file mode 100644
index 000000000..d8bf66603
--- /dev/null
+++ b/src/tools/clippy/tests/ui/op_ref.rs
@@ -0,0 +1,94 @@
+#![allow(unused_variables, clippy::blacklisted_name)]
+#![warn(clippy::op_ref)]
+use std::collections::HashSet;
+use std::ops::{BitAnd, Mul};
+
+fn main() {
+ let tracked_fds: HashSet<i32> = HashSet::new();
+ let new_fds = HashSet::new();
+ let unwanted = &tracked_fds - &new_fds;
+
+ let foo = &5 - &6;
+
+ let bar = String::new();
+ let bar = "foo" == &bar;
+
+ let a = "a".to_string();
+ let b = "a";
+
+ if b < &a {
+ println!("OK");
+ }
+
+ struct X(i32);
+ impl BitAnd for X {
+ type Output = X;
+ fn bitand(self, rhs: X) -> X {
+ X(self.0 & rhs.0)
+ }
+ }
+ impl<'a> BitAnd<&'a X> for X {
+ type Output = X;
+ fn bitand(self, rhs: &'a X) -> X {
+ X(self.0 & rhs.0)
+ }
+ }
+ let x = X(1);
+ let y = X(2);
+ let z = x & &y;
+
+ #[derive(Copy, Clone)]
+ struct Y(i32);
+ impl BitAnd for Y {
+ type Output = Y;
+ fn bitand(self, rhs: Y) -> Y {
+ Y(self.0 & rhs.0)
+ }
+ }
+ impl<'a> BitAnd<&'a Y> for Y {
+ type Output = Y;
+ fn bitand(self, rhs: &'a Y) -> Y {
+ Y(self.0 & rhs.0)
+ }
+ }
+ let x = Y(1);
+ let y = Y(2);
+ let z = x & &y;
+}
+
+#[derive(Clone, Copy)]
+struct A(i32);
+#[derive(Clone, Copy)]
+struct B(i32);
+
+impl Mul<&A> for B {
+ type Output = i32;
+ fn mul(self, rhs: &A) -> Self::Output {
+ self.0 * rhs.0
+ }
+}
+impl Mul<A> for B {
+ type Output = i32;
+ fn mul(self, rhs: A) -> Self::Output {
+ // Should not lint because removing the reference would lead to unconditional recursion
+ self * &rhs
+ }
+}
+impl Mul<&A> for A {
+ type Output = i32;
+ fn mul(self, rhs: &A) -> Self::Output {
+ self.0 * rhs.0
+ }
+}
+impl Mul<A> for A {
+ type Output = i32;
+ fn mul(self, rhs: A) -> Self::Output {
+ let one = B(1);
+ let two = 2;
+ let three = 3;
+ let _ = one * &self;
+ let _ = two + &three;
+ // Removing the reference would lead to unconditional recursion
+ self * &rhs
+ }
+}
diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr
new file mode 100644
index 000000000..fe36c0116
--- /dev/null
+++ b/src/tools/clippy/tests/ui/op_ref.stderr
@@ -0,0 +1,38 @@
+error: needlessly taken reference of both operands
+ --> $DIR/op_ref.rs:11:15
+ |
+LL | let foo = &5 - &6;
+ | ^^^^^^^
+ |
+ = note: `-D clippy::op-ref` implied by `-D warnings`
+help: use the values directly
+ |
+LL | let foo = 5 - 6;
+ | ~ ~
+
+error: taken reference of right operand
+ --> $DIR/op_ref.rs:56:13
+ |
+LL | let z = x & &y;
+ | ^^^^--
+ | |
+ | help: use the right value directly: `y`
+
+error: taken reference of right operand
+ --> $DIR/op_ref.rs:89:17
+ |
+LL | let _ = one * &self;
+ | ^^^^^^-----
+ | |
+ | help: use the right value directly: `self`
+
+error: taken reference of right operand
+ --> $DIR/op_ref.rs:90:17
+ |
+LL | let _ = two + &three;
+ | ^^^^^^------
+ | |
+ | help: use the right value directly: `three`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/open_options.rs b/src/tools/clippy/tests/ui/open_options.rs
new file mode 100644
index 000000000..9063fafbc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/open_options.rs
@@ -0,0 +1,14 @@
+use std::fs::OpenOptions;
+
+#[allow(unused_must_use)]
+#[warn(clippy::nonsensical_open_options)]
+fn main() {
+ OpenOptions::new().read(true).truncate(true).open("foo.txt");
+ OpenOptions::new().append(true).truncate(true).open("foo.txt");
+
+ OpenOptions::new().read(true).read(false).open("foo.txt");
+ OpenOptions::new().create(true).create(false).open("foo.txt");
+ OpenOptions::new().write(true).write(false).open("foo.txt");
+ OpenOptions::new().append(true).append(false).open("foo.txt");
+ OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
+}
diff --git a/src/tools/clippy/tests/ui/open_options.stderr b/src/tools/clippy/tests/ui/open_options.stderr
new file mode 100644
index 000000000..26fe9f6fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/open_options.stderr
@@ -0,0 +1,46 @@
+error: file opened with `truncate` and `read`
+ --> $DIR/open_options.rs:6:5
+ |
+LL | OpenOptions::new().read(true).truncate(true).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::nonsensical-open-options` implied by `-D warnings`
+
+error: file opened with `append` and `truncate`
+ --> $DIR/open_options.rs:7:5
+ |
+LL | OpenOptions::new().append(true).truncate(true).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the method `read` is called more than once
+ --> $DIR/open_options.rs:9:5
+ |
+LL | OpenOptions::new().read(true).read(false).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the method `create` is called more than once
+ --> $DIR/open_options.rs:10:5
+ |
+LL | OpenOptions::new().create(true).create(false).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the method `write` is called more than once
+ --> $DIR/open_options.rs:11:5
+ |
+LL | OpenOptions::new().write(true).write(false).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the method `append` is called more than once
+ --> $DIR/open_options.rs:12:5
+ |
+LL | OpenOptions::new().append(true).append(false).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the method `truncate` is called more than once
+ --> $DIR/open_options.rs:13:5
+ |
+LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
new file mode 100644
index 000000000..07d7f0b45
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed
@@ -0,0 +1,44 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::redundant_clone)]
+#![warn(clippy::option_as_ref_deref)]
+
+use std::ffi::{CString, OsString};
+use std::ops::{Deref, DerefMut};
+use std::path::PathBuf;
+
+fn main() {
+ let mut opt = Some(String::from("123"));
+
+ let _ = opt.clone().as_deref().map(str::len);
+
+ #[rustfmt::skip]
+ let _ = opt.clone().as_deref()
+ .map(str::len);
+
+ let _ = opt.as_deref_mut();
+
+ let _ = opt.as_deref();
+ let _ = opt.as_deref();
+ let _ = opt.as_deref_mut();
+ let _ = opt.as_deref_mut();
+ let _ = Some(CString::new(vec![]).unwrap()).as_deref();
+ let _ = Some(OsString::new()).as_deref();
+ let _ = Some(PathBuf::new()).as_deref();
+ let _ = Some(Vec::<()>::new()).as_deref();
+ let _ = Some(Vec::<()>::new()).as_deref_mut();
+
+ let _ = opt.as_deref();
+ let _ = opt.clone().as_deref_mut().map(|x| x.len());
+
+ let vc = vec![String::new()];
+ let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
+
+ let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
+
+ let _ = opt.as_deref();
+ let _ = opt.as_deref_mut();
+
+ // Issue #5927
+ let _ = opt.as_deref();
+}
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
new file mode 100644
index 000000000..6ae059c94
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs
@@ -0,0 +1,47 @@
+// run-rustfix
+
+#![allow(unused_imports, clippy::redundant_clone)]
+#![warn(clippy::option_as_ref_deref)]
+
+use std::ffi::{CString, OsString};
+use std::ops::{Deref, DerefMut};
+use std::path::PathBuf;
+
+fn main() {
+ let mut opt = Some(String::from("123"));
+
+ let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
+
+ #[rustfmt::skip]
+ let _ = opt.clone()
+ .as_ref().map(
+ Deref::deref
+ )
+ .map(str::len);
+
+ let _ = opt.as_mut().map(DerefMut::deref_mut);
+
+ let _ = opt.as_ref().map(String::as_str);
+ let _ = opt.as_ref().map(|x| x.as_str());
+ let _ = opt.as_mut().map(String::as_mut_str);
+ let _ = opt.as_mut().map(|x| x.as_mut_str());
+ let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
+ let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
+ let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
+ let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
+ let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
+
+ let _ = opt.as_ref().map(|x| x.deref());
+ let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
+
+ let vc = vec![String::new()];
+ let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
+
+ let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
+
+ let _ = opt.as_ref().map(|x| &**x);
+ let _ = opt.as_mut().map(|x| &mut **x);
+
+ // Issue #5927
+ let _ = opt.as_ref().map(std::ops::Deref::deref);
+}
diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr
new file mode 100644
index 000000000..62f282324
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr
@@ -0,0 +1,110 @@
+error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:13:13
+ |
+LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
+ |
+ = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
+
+error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:16:13
+ |
+LL | let _ = opt.clone()
+ | _____________^
+LL | | .as_ref().map(
+LL | | Deref::deref
+LL | | )
+ | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
+
+error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:22:13
+ |
+LL | let _ = opt.as_mut().map(DerefMut::deref_mut);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
+
+error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:24:13
+ |
+LL | let _ = opt.as_ref().map(String::as_str);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:25:13
+ |
+LL | let _ = opt.as_ref().map(|x| x.as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:26:13
+ |
+LL | let _ = opt.as_mut().map(String::as_mut_str);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
+
+error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:27:13
+ |
+LL | let _ = opt.as_mut().map(|x| x.as_mut_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
+
+error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:28:13
+ |
+LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
+
+error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:29:13
+ |
+LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
+
+error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:30:13
+ |
+LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
+
+error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:31:13
+ |
+LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
+
+error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:32:13
+ |
+LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
+
+error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:34:13
+ |
+LL | let _ = opt.as_ref().map(|x| x.deref());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:35:13
+ |
+LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
+
+error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:42:13
+ |
+LL | let _ = opt.as_ref().map(|x| &**x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
+ --> $DIR/option_as_ref_deref.rs:43:13
+ |
+LL | let _ = opt.as_mut().map(|x| &mut **x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
+
+error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+ --> $DIR/option_as_ref_deref.rs:46:13
+ |
+LL | let _ = opt.as_ref().map(std::ops::Deref::deref);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs
new file mode 100644
index 000000000..0141fb785
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs
@@ -0,0 +1,24 @@
+// aux-build:macro_rules.rs
+#![warn(clippy::option_env_unwrap)]
+#![allow(clippy::map_flatten)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! option_env_unwrap {
+ ($env: expr) => {
+ option_env!($env).unwrap()
+ };
+ ($env: expr, $message: expr) => {
+ option_env!($env).expect($message)
+ };
+}
+
+fn main() {
+ let _ = option_env!("PATH").unwrap();
+ let _ = option_env!("PATH").expect("environment variable PATH isn't set");
+ let _ = option_env_unwrap!("PATH");
+ let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
+ let _ = option_env_unwrap_external!("PATH");
+ let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
+}
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
new file mode 100644
index 000000000..885ac096c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
@@ -0,0 +1,61 @@
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:18:13
+ |
+LL | let _ = option_env!("PATH").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::option-env-unwrap` implied by `-D warnings`
+ = help: consider using the `env!` macro instead
+
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:19:13
+ |
+LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the `env!` macro instead
+
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:10:9
+ |
+LL | option_env!($env).unwrap()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | let _ = option_env_unwrap!("PATH");
+ | -------------------------- in this macro invocation
+ |
+ = help: consider using the `env!` macro instead
+ = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:13:9
+ |
+LL | option_env!($env).expect($message)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
+ | ----------------------------------------------------------------- in this macro invocation
+ |
+ = help: consider using the `env!` macro instead
+ = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:22:13
+ |
+LL | let _ = option_env_unwrap_external!("PATH");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the `env!` macro instead
+ = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this will panic at run-time if the environment variable doesn't exist at compile-time
+ --> $DIR/option_env_unwrap.rs:23:13
+ |
+LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the `env!` macro instead
+ = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_filter_map.fixed b/src/tools/clippy/tests/ui/option_filter_map.fixed
new file mode 100644
index 000000000..b20f73f31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_filter_map.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
+
+fn main() {
+ let _ = Some(Some(1)).flatten();
+ let _ = Some(Some(1)).flatten();
+ let _ = Some(1).map(odds_out).flatten();
+ let _ = Some(1).map(odds_out).flatten();
+
+ let _ = vec![Some(1)].into_iter().flatten();
+ let _ = vec![Some(1)].into_iter().flatten();
+ let _ = vec![1]
+ .into_iter()
+ .map(odds_out)
+ .flatten();
+ let _ = vec![1]
+ .into_iter()
+ .map(odds_out)
+ .flatten();
+}
+
+fn odds_out(x: i32) -> Option<i32> {
+ if x % 2 == 0 { Some(x) } else { None }
+}
diff --git a/src/tools/clippy/tests/ui/option_filter_map.rs b/src/tools/clippy/tests/ui/option_filter_map.rs
new file mode 100644
index 000000000..7abaaa0fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_filter_map.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
+
+fn main() {
+ let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
+ let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
+ let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
+ let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
+
+ let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
+ let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
+ let _ = vec![1]
+ .into_iter()
+ .map(odds_out)
+ .filter(Option::is_some)
+ .map(Option::unwrap);
+ let _ = vec![1]
+ .into_iter()
+ .map(odds_out)
+ .filter(|o| o.is_some())
+ .map(|o| o.unwrap());
+}
+
+fn odds_out(x: i32) -> Option<i32> {
+ if x % 2 == 0 { Some(x) } else { None }
+}
diff --git a/src/tools/clippy/tests/ui/option_filter_map.stderr b/src/tools/clippy/tests/ui/option_filter_map.stderr
new file mode 100644
index 000000000..4a030ac9a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_filter_map.stderr
@@ -0,0 +1,56 @@
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:6:27
+ |
+LL | let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+ |
+ = note: `-D clippy::option-filter-map` implied by `-D warnings`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:7:27
+ |
+LL | let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:8:35
+ |
+LL | let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:9:35
+ |
+LL | let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:11:39
+ |
+LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:12:39
+ |
+LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:16:10
+ |
+LL | .filter(Option::is_some)
+ | __________^
+LL | | .map(Option::unwrap);
+ | |____________________________^ help: consider using `flatten` instead: `flatten()`
+
+error: `filter` for `Some` followed by `unwrap`
+ --> $DIR/option_filter_map.rs:21:10
+ |
+LL | .filter(|o| o.is_some())
+ | __________^
+LL | | .map(|o| o.unwrap());
+ | |____________________________^ help: consider using `flatten` instead: `flatten()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed
new file mode 100644
index 000000000..b6d5e106f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -0,0 +1,182 @@
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(
+ unused_tuple_struct_fields,
+ clippy::redundant_closure,
+ clippy::ref_option_ref,
+ clippy::equatable_if_let,
+ clippy::let_unit_value
+)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ string.map_or((false, "hello"), |x| (true, x))
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = string.map_or(0, |s| s.len());
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+ let _ = num.as_ref().map_or(&0, |s| s);
+ let _ = num.map_or(0, |mut s| {
+ s += 1;
+ s
+ });
+ let _ = num.as_mut().map_or(&mut 0, |s| {
+ *s += 1;
+ s
+ });
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ arg.map_or(13, |x| {
+ let y = x * x;
+ y * y
+ })
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = arg.map_or_else(|| side_effect(), |x| x);
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = arg.map_or_else(|| {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ }, |x| x * x * x * x);
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+ pattern
+ .trim_matches('/')
+ .split('/')
+ .flat_map(|s| {
+ s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
+ })
+ .collect::<Vec<_>>()
+}
+
+enum DummyEnum {
+ One(u8),
+ Two,
+}
+
+// should not warn since there is a compled complex subpat
+// see #7991
+fn complex_subpat() -> DummyEnum {
+ let x = Some(DummyEnum::One(1));
+ let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
+ DummyEnum::Two
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = optional.map_or(5, |x| x + 2);
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = Some(0).map_or(0, |x| loop {
+ if x == 0 {
+ break x;
+ }
+ });
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = Some(0).map_or(s.len(), |x| s.len() + x);
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = Some(0).map_or(1, |x| {
+ let s = s;
+ s.len() + x
+ });
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+
+ let _ = pattern_to_vec("hello world");
+ let _ = complex_subpat();
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs
new file mode 100644
index 000000000..35bae1593
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -0,0 +1,211 @@
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+#![allow(
+ unused_tuple_struct_fields,
+ clippy::redundant_closure,
+ clippy::ref_option_ref,
+ clippy::equatable_if_let,
+ clippy::let_unit_value
+)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+ if let Some(x) = string {
+ (true, x)
+ } else {
+ (false, "hello")
+ }
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+ if string.is_none() {
+ None
+ } else if let Some(x) = string {
+ Some((true, x))
+ } else {
+ Some((false, ""))
+ }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+ let _ = if let Some(s) = *string { s.len() } else { 0 };
+ let _ = if let Some(s) = &num { s } else { &0 };
+ let _ = if let Some(s) = &mut num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+ let _ = if let Some(ref s) = num { s } else { &0 };
+ let _ = if let Some(mut s) = num {
+ s += 1;
+ s
+ } else {
+ 0
+ };
+ let _ = if let Some(ref mut s) = num {
+ *s += 1;
+ s
+ } else {
+ &mut 0
+ };
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+ if let Some(x) = arg {
+ let y = x * x;
+ y * y
+ } else {
+ 13
+ }
+}
+
+fn impure_else(arg: Option<i32>) {
+ let side_effect = || {
+ println!("return 1");
+ 1
+ };
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ // map_or_else must be suggested
+ side_effect()
+ };
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+ let _ = if let Some(x) = arg {
+ x * x * x * x
+ } else {
+ let mut y = 1;
+ y = (y + 2 / y) / 2;
+ y = (y + 2 / y) / 2;
+ y
+ };
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+ let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+ for _ in 0..10 {
+ let _ = if let Some(x) = arg {
+ x
+ } else {
+ continue;
+ };
+ }
+ let _ = if let Some(x) = arg {
+ return x;
+ } else {
+ 5
+ };
+ 7
+}
+
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+ pattern
+ .trim_matches('/')
+ .split('/')
+ .flat_map(|s| {
+ if let Some(idx) = s.find('.') {
+ vec![s[..idx].to_string(), s[idx..].to_string()]
+ } else {
+ vec![s.to_string()]
+ }
+ })
+ .collect::<Vec<_>>()
+}
+
+enum DummyEnum {
+ One(u8),
+ Two,
+}
+
+// should not warn since there is a compled complex subpat
+// see #7991
+fn complex_subpat() -> DummyEnum {
+ let x = Some(DummyEnum::One(1));
+ let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
+ DummyEnum::Two
+}
+
+fn main() {
+ let optional = Some(5);
+ let _ = if let Some(x) = optional { x + 2 } else { 5 };
+ let _ = bad1(None);
+ let _ = else_if_option(None);
+ unop_bad(&None, None);
+ let _ = longer_body(None);
+ test_map_or_else(None);
+ let _ = negative_tests(None);
+ let _ = impure_else(None);
+
+ let _ = if let Some(x) = Some(0) {
+ loop {
+ if x == 0 {
+ break x;
+ }
+ }
+ } else {
+ 0
+ };
+
+ // #7576
+ const fn _f(x: Option<u32>) -> u32 {
+ // Don't lint, `map_or` isn't const
+ if let Some(x) = x { x } else { 10 }
+ }
+
+ // #5822
+ let s = String::new();
+ // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ s.len()
+ };
+
+ let s = String::new();
+ // Lint, both branches immutably borrow `s`.
+ let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+
+ let s = String::new();
+ // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+ let _ = if let Some(x) = Some(0) {
+ let s = s;
+ s.len() + x
+ } else {
+ 1
+ };
+
+ let s = Some(String::new());
+ // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+ let _ = if let Some(x) = &s {
+ x.len()
+ } else {
+ let _s = s;
+ 10
+ };
+
+ let mut s = Some(String::new());
+ // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s`
+ let _ = if let Some(x) = &mut s {
+ x.push_str("test");
+ x.len()
+ } else {
+ let _s = &s;
+ 10
+ };
+
+ async fn _f1(x: u32) -> u32 {
+ x
+ }
+
+ async fn _f2() {
+ // Don't lint. `await` can't be moved into a closure.
+ let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+ }
+
+ let _ = pattern_to_vec("hello world");
+ let _ = complex_subpat();
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr
new file mode 100644
index 000000000..daba60600
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -0,0 +1,210 @@
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:12:5
+ |
+LL | / if let Some(x) = string {
+LL | | (true, x)
+LL | | } else {
+LL | | (false, "hello")
+LL | | }
+ | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))`
+ |
+ = note: `-D clippy::option-if-let-else` implied by `-D warnings`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:30:13
+ |
+LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:31:13
+ |
+LL | let _ = if let Some(s) = &num { s } else { &0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:32:13
+ |
+LL | let _ = if let Some(s) = &mut num {
+ | _____________^
+LL | | *s += 1;
+LL | | s
+LL | | } else {
+LL | | &mut 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.as_mut().map_or(&mut 0, |s| {
+LL + *s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:38:13
+ |
+LL | let _ = if let Some(ref s) = num { s } else { &0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:39:13
+ |
+LL | let _ = if let Some(mut s) = num {
+ | _____________^
+LL | | s += 1;
+LL | | s
+LL | | } else {
+LL | | 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.map_or(0, |mut s| {
+LL + s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:45:13
+ |
+LL | let _ = if let Some(ref mut s) = num {
+ | _____________^
+LL | | *s += 1;
+LL | | s
+LL | | } else {
+LL | | &mut 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = num.as_mut().map_or(&mut 0, |s| {
+LL + *s += 1;
+LL + s
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:54:5
+ |
+LL | / if let Some(x) = arg {
+LL | | let y = x * x;
+LL | | y * y
+LL | | } else {
+LL | | 13
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ arg.map_or(13, |x| {
+LL + let y = x * x;
+LL + y * y
+LL + })
+ |
+
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:67:13
+ |
+LL | let _ = if let Some(x) = arg {
+ | _____________^
+LL | | x
+LL | | } else {
+LL | | // map_or_else must be suggested
+LL | | side_effect()
+LL | | };
+ | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
+
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:76:13
+ |
+LL | let _ = if let Some(x) = arg {
+ | _____________^
+LL | | x * x * x * x
+LL | | } else {
+LL | | let mut y = 1;
+... |
+LL | | y
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = arg.map_or_else(|| {
+LL + let mut y = 1;
+LL + y = (y + 2 / y) / 2;
+LL + y = (y + 2 / y) / 2;
+LL + y
+LL ~ }, |x| x * x * x * x);
+ |
+
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:109:13
+ |
+LL | / if let Some(idx) = s.find('.') {
+LL | | vec![s[..idx].to_string(), s[idx..].to_string()]
+LL | | } else {
+LL | | vec![s.to_string()]
+LL | | }
+ | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:133:13
+ |
+LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:142:13
+ |
+LL | let _ = if let Some(x) = Some(0) {
+ | _____________^
+LL | | loop {
+LL | | if x == 0 {
+LL | | break x;
+... |
+LL | | 0
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = Some(0).map_or(0, |x| loop {
+LL + if x == 0 {
+LL + break x;
+LL + }
+LL ~ });
+ |
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:170:13
+ |
+LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
+
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:174:13
+ |
+LL | let _ = if let Some(x) = Some(0) {
+ | _____________^
+LL | | let s = s;
+LL | | s.len() + x
+LL | | } else {
+LL | | 1
+LL | | };
+ | |_____^
+ |
+help: try
+ |
+LL ~ let _ = Some(0).map_or(1, |x| {
+LL + let s = s;
+LL + s.len() + x
+LL ~ });
+ |
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.fixed b/src/tools/clippy/tests/ui/option_map_or_none.fixed
new file mode 100644
index 000000000..04bfac773
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_or_none.fixed
@@ -0,0 +1,26 @@
+// run-rustfix
+
+#![allow(clippy::bind_instead_of_map)]
+
+fn main() {
+ let opt = Some(1);
+ let r: Result<i32, i32> = Ok(1);
+ let bar = |_| Some(1);
+
+ // Check `OPTION_MAP_OR_NONE`.
+ // Single line case.
+ let _: Option<i32> = opt.map(|x| x + 1);
+ // Multi-line case.
+ #[rustfmt::skip]
+ let _: Option<i32> = opt.map(|x| x + 1);
+ // function returning `Option`
+ let _: Option<i32> = opt.and_then(bar);
+ let _: Option<i32> = opt.and_then(|x| {
+ let offset = 0;
+ let height = x;
+ Some(offset + height)
+ });
+
+ // Check `RESULT_MAP_OR_INTO_OPTION`.
+ let _: Option<i32> = r.ok();
+}
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.rs b/src/tools/clippy/tests/ui/option_map_or_none.rs
new file mode 100644
index 000000000..bb84f8a48
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_or_none.rs
@@ -0,0 +1,28 @@
+// run-rustfix
+
+#![allow(clippy::bind_instead_of_map)]
+
+fn main() {
+ let opt = Some(1);
+ let r: Result<i32, i32> = Ok(1);
+ let bar = |_| Some(1);
+
+ // Check `OPTION_MAP_OR_NONE`.
+ // Single line case.
+ let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
+ // Multi-line case.
+ #[rustfmt::skip]
+ let _: Option<i32> = opt.map_or(None, |x| {
+ Some(x + 1)
+ });
+ // function returning `Option`
+ let _: Option<i32> = opt.map_or(None, bar);
+ let _: Option<i32> = opt.map_or(None, |x| {
+ let offset = 0;
+ let height = x;
+ Some(offset + height)
+ });
+
+ // Check `RESULT_MAP_OR_INTO_OPTION`.
+ let _: Option<i32> = r.map_or(None, Some);
+}
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr
new file mode 100644
index 000000000..7befcb890
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_or_none.stderr
@@ -0,0 +1,53 @@
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+ --> $DIR/option_map_or_none.rs:12:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)`
+ |
+ = note: `-D clippy::option-map-or-none` implied by `-D warnings`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+ --> $DIR/option_map_or_none.rs:15:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, |x| {
+ | __________________________^
+LL | | Some(x + 1)
+LL | | });
+ | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+ --> $DIR/option_map_or_none.rs:19:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, bar);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+ --> $DIR/option_map_or_none.rs:20:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, |x| {
+ | __________________________^
+LL | | let offset = 0;
+LL | | let height = x;
+LL | | Some(offset + height)
+LL | | });
+ | |______^
+ |
+help: try using `and_then` instead
+ |
+LL ~ let _: Option<i32> = opt.and_then(|x| {
+LL + let offset = 0;
+LL + let height = x;
+LL + Some(offset + height)
+LL ~ });
+ |
+
+error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
+ --> $DIR/option_map_or_none.rs:27:26
+ |
+LL | let _: Option<i32> = r.map_or(None, Some);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()`
+ |
+ = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
new file mode 100644
index 000000000..1290bd8ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
@@ -0,0 +1,88 @@
+// run-rustfix
+
+#![warn(clippy::option_map_unit_fn)]
+#![allow(unused)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+fn option() -> Option<usize> {
+ Some(10)
+}
+
+struct HasOption {
+ field: Option<usize>,
+}
+
+impl HasOption {
+ fn do_option_nothing(&self, value: usize) {}
+
+ fn do_option_plus_one(&self, value: usize) -> usize {
+ value + 1
+ }
+}
+#[rustfmt::skip]
+fn option_map_unit_fn() {
+ let x = HasOption { field: Some(10) };
+
+ x.field.map(plus_one);
+ let _ : Option<()> = x.field.map(do_nothing);
+
+ if let Some(x_field) = x.field { do_nothing(x_field) }
+
+ if let Some(x_field) = x.field { do_nothing(x_field) }
+
+ if let Some(x_field) = x.field { diverge(x_field) }
+
+ let captured = 10;
+ if let Some(value) = x.field { do_nothing(value + captured) };
+ let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
+
+ if let Some(value) = x.field { x.do_option_nothing(value + captured) }
+
+ if let Some(value) = x.field { x.do_option_plus_one(value + captured); }
+
+
+ if let Some(value) = x.field { do_nothing(value + captured) }
+
+ if let Some(value) = x.field { do_nothing(value + captured) }
+
+ if let Some(value) = x.field { do_nothing(value + captured); }
+
+ if let Some(value) = x.field { do_nothing(value + captured); }
+
+
+ if let Some(value) = x.field { diverge(value + captured) }
+
+ if let Some(value) = x.field { diverge(value + captured) }
+
+ if let Some(value) = x.field { diverge(value + captured); }
+
+ if let Some(value) = x.field { diverge(value + captured); }
+
+
+ x.field.map(|value| plus_one(value + captured));
+ x.field.map(|value| { plus_one(value + captured) });
+ if let Some(value) = x.field { let y = plus_one(value + captured); }
+
+ if let Some(value) = x.field { plus_one(value + captured); }
+
+ if let Some(value) = x.field { plus_one(value + captured); }
+
+
+ if let Some(ref value) = x.field { do_nothing(value + captured) }
+
+ if let Some(a) = option() { do_nothing(a) }
+
+ if let Some(value) = option() { println!("{:?}", value) }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
new file mode 100644
index 000000000..f3e5b62c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
@@ -0,0 +1,88 @@
+// run-rustfix
+
+#![warn(clippy::option_map_unit_fn)]
+#![allow(unused)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+fn option() -> Option<usize> {
+ Some(10)
+}
+
+struct HasOption {
+ field: Option<usize>,
+}
+
+impl HasOption {
+ fn do_option_nothing(&self, value: usize) {}
+
+ fn do_option_plus_one(&self, value: usize) -> usize {
+ value + 1
+ }
+}
+#[rustfmt::skip]
+fn option_map_unit_fn() {
+ let x = HasOption { field: Some(10) };
+
+ x.field.map(plus_one);
+ let _ : Option<()> = x.field.map(do_nothing);
+
+ x.field.map(do_nothing);
+
+ x.field.map(do_nothing);
+
+ x.field.map(diverge);
+
+ let captured = 10;
+ if let Some(value) = x.field { do_nothing(value + captured) };
+ let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
+
+ x.field.map(|value| x.do_option_nothing(value + captured));
+
+ x.field.map(|value| { x.do_option_plus_one(value + captured); });
+
+
+ x.field.map(|value| do_nothing(value + captured));
+
+ x.field.map(|value| { do_nothing(value + captured) });
+
+ x.field.map(|value| { do_nothing(value + captured); });
+
+ x.field.map(|value| { { do_nothing(value + captured); } });
+
+
+ x.field.map(|value| diverge(value + captured));
+
+ x.field.map(|value| { diverge(value + captured) });
+
+ x.field.map(|value| { diverge(value + captured); });
+
+ x.field.map(|value| { { diverge(value + captured); } });
+
+
+ x.field.map(|value| plus_one(value + captured));
+ x.field.map(|value| { plus_one(value + captured) });
+ x.field.map(|value| { let y = plus_one(value + captured); });
+
+ x.field.map(|value| { plus_one(value + captured); });
+
+ x.field.map(|value| { { plus_one(value + captured); } });
+
+
+ x.field.map(|ref value| { do_nothing(value + captured) });
+
+ option().map(do_nothing);
+
+ option().map(|value| println!("{:?}", value));
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
new file mode 100644
index 000000000..ab2a294a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
@@ -0,0 +1,156 @@
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:39:5
+ |
+LL | x.field.map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
+ |
+ = note: `-D clippy::option-map-unit-fn` implied by `-D warnings`
+
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:41:5
+ |
+LL | x.field.map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
+
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:43:5
+ |
+LL | x.field.map(diverge);
+ | ^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:49:5
+ |
+LL | x.field.map(|value| x.do_option_nothing(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:51:5
+ |
+LL | x.field.map(|value| { x.do_option_plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:54:5
+ |
+LL | x.field.map(|value| do_nothing(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:56:5
+ |
+LL | x.field.map(|value| { do_nothing(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:58:5
+ |
+LL | x.field.map(|value| { do_nothing(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:60:5
+ |
+LL | x.field.map(|value| { { do_nothing(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:63:5
+ |
+LL | x.field.map(|value| diverge(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:65:5
+ |
+LL | x.field.map(|value| { diverge(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:67:5
+ |
+LL | x.field.map(|value| { diverge(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:69:5
+ |
+LL | x.field.map(|value| { { diverge(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:74:5
+ |
+LL | x.field.map(|value| { let y = plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:76:5
+ |
+LL | x.field.map(|value| { plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:78:5
+ |
+LL | x.field.map(|value| { { plus_one(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:81:5
+ |
+LL | x.field.map(|ref value| { do_nothing(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:83:5
+ |
+LL | option().map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(a) = option() { do_nothing(a) }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/option_map_unit_fn_fixable.rs:85:5
+ |
+LL | option().map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = option() { println!("{:?}", value) }`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs
new file mode 100644
index 000000000..20e6c15b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs
@@ -0,0 +1,39 @@
+#![warn(clippy::option_map_unit_fn)]
+#![allow(unused)]
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+#[rustfmt::skip]
+fn option_map_unit_fn() {
+
+ x.field.map(|value| { do_nothing(value); do_nothing(value) });
+
+ x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
+
+ // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
+ // proper suggestion for these cases
+ x.field.map(|value| {
+ do_nothing(value);
+ do_nothing(value)
+ });
+ x.field.map(|value| { do_nothing(value); do_nothing(value); });
+
+ // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them
+ Some(42).map(diverge);
+ "12".parse::<i32>().ok().map(diverge);
+ Some(plus_one(1)).map(do_nothing);
+
+ // Should suggest `if let Some(_y) ...` to not override the existing foo variable
+ let y = Some(42);
+ y.map(do_nothing);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr
new file mode 100644
index 000000000..a53f5889c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr
@@ -0,0 +1,27 @@
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/option_map_unit_fn_unfixable.rs:17:5
+ |
+LL | x.field.map(|value| { do_nothing(value); do_nothing(value) });
+ | ^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/option_map_unit_fn_unfixable.rs:19:5
+ |
+LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
+ | ^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/option_map_unit_fn_unfixable.rs:23:5
+ |
+LL | x.field.map(|value| {
+ | ^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/option_map_unit_fn_unfixable.rs:27:5
+ |
+LL | x.field.map(|value| { do_nothing(value); do_nothing(value); });
+ | ^ not found in this scope
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs
new file mode 100644
index 000000000..2faab9e03
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_option.rs
@@ -0,0 +1,89 @@
+#![deny(clippy::option_option)]
+#![allow(clippy::unnecessary_wraps)]
+
+const C: Option<Option<i32>> = None;
+static S: Option<Option<i32>> = None;
+
+fn input(_: Option<Option<u8>>) {}
+
+fn output() -> Option<Option<u8>> {
+ None
+}
+
+fn output_nested() -> Vec<Option<Option<u8>>> {
+ vec![None]
+}
+
+// The lint only generates one warning for this
+fn output_nested_nested() -> Option<Option<Option<u8>>> {
+ None
+}
+
+struct Struct {
+ x: Option<Option<u8>>,
+}
+
+impl Struct {
+ fn struct_fn() -> Option<Option<u8>> {
+ None
+ }
+}
+
+trait Trait {
+ fn trait_fn() -> Option<Option<u8>>;
+}
+
+enum Enum {
+ Tuple(Option<Option<u8>>),
+ Struct { x: Option<Option<u8>> },
+}
+
+// The lint allows this
+type OptionOption = Option<Option<u32>>;
+
+// The lint allows this
+fn output_type_alias() -> OptionOption {
+ None
+}
+
+// The line allows this
+impl Trait for Struct {
+ fn trait_fn() -> Option<Option<u8>> {
+ None
+ }
+}
+
+fn main() {
+ input(None);
+ output();
+ output_nested();
+
+ // The lint allows this
+ let local: Option<Option<u8>> = None;
+
+ // The lint allows this
+ let expr = Some(Some(true));
+}
+
+extern crate serde;
+mod issue_4298 {
+ use serde::{Deserialize, Deserializer, Serialize};
+ use std::borrow::Cow;
+
+ #[derive(Serialize, Deserialize)]
+ struct Foo<'a> {
+ #[serde(deserialize_with = "func")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(default)]
+ #[serde(borrow)]
+ foo: Option<Option<Cow<'a, str>>>,
+ }
+
+ #[allow(clippy::option_option)]
+ fn func<'a, D>(_: D) -> Result<Option<Option<Cow<'a, str>>>, D::Error>
+ where
+ D: Deserializer<'a>,
+ {
+ Ok(Some(Some(Cow::Borrowed("hi"))))
+ }
+}
diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr
new file mode 100644
index 000000000..a925bb35b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_option.stderr
@@ -0,0 +1,80 @@
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:4:10
+ |
+LL | const C: Option<Option<i32>> = None;
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/option_option.rs:1:9
+ |
+LL | #![deny(clippy::option_option)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:5:11
+ |
+LL | static S: Option<Option<i32>> = None;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:7:13
+ |
+LL | fn input(_: Option<Option<u8>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:9:16
+ |
+LL | fn output() -> Option<Option<u8>> {
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:13:27
+ |
+LL | fn output_nested() -> Vec<Option<Option<u8>>> {
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:18:30
+ |
+LL | fn output_nested_nested() -> Option<Option<Option<u8>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:23:8
+ |
+LL | x: Option<Option<u8>>,
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:27:23
+ |
+LL | fn struct_fn() -> Option<Option<u8>> {
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:33:22
+ |
+LL | fn trait_fn() -> Option<Option<u8>>;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:37:11
+ |
+LL | Tuple(Option<Option<u8>>),
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:38:17
+ |
+LL | Struct { x: Option<Option<u8>> },
+ | ^^^^^^^^^^^^^^^^^^
+
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+ --> $DIR/option_option.rs:79:14
+ |
+LL | foo: Option<Option<Cow<'a, str>>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_take_on_temporary.fixed b/src/tools/clippy/tests/ui/option_take_on_temporary.fixed
new file mode 100644
index 000000000..29691e816
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_take_on_temporary.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+}
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
new file mode 100644
index 000000000..fdb08d953
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -0,0 +1,229 @@
+// run-rustfix
+
+#![warn(clippy::or_fun_call)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::time::Duration;
+
+/// Checks implementation of the `OR_FUN_CALL` lint.
+fn or_fun_call() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo
+ }
+ }
+
+ struct FakeDefault;
+ impl FakeDefault {
+ fn default() -> Self {
+ FakeDefault
+ }
+ }
+
+ impl Default for FakeDefault {
+ fn default() -> Self {
+ FakeDefault
+ }
+ }
+
+ enum Enum {
+ A(i32),
+ }
+
+ fn make<T>() -> T {
+ unimplemented!();
+ }
+
+ let with_enum = Some(Enum::A(1));
+ with_enum.unwrap_or(Enum::A(5));
+
+ let with_const_fn = Some(Duration::from_secs(1));
+ with_const_fn.unwrap_or(Duration::from_secs(5));
+
+ let with_constructor = Some(vec![1]);
+ with_constructor.unwrap_or_else(make);
+
+ let with_new = Some(vec![1]);
+ with_new.unwrap_or_default();
+
+ let with_const_args = Some(vec![1]);
+ with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
+
+ let with_err: Result<_, ()> = Ok(vec![1]);
+ with_err.unwrap_or_else(|_| make());
+
+ let with_err_args: Result<_, ()> = Ok(vec![1]);
+ with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
+
+ let with_default_trait = Some(1);
+ with_default_trait.unwrap_or_default();
+
+ let with_default_type = Some(1);
+ with_default_type.unwrap_or_default();
+
+ let self_default = None::<FakeDefault>;
+ self_default.unwrap_or_else(<FakeDefault>::default);
+
+ let real_default = None::<FakeDefault>;
+ real_default.unwrap_or_default();
+
+ let with_vec = Some(vec![1]);
+ with_vec.unwrap_or_default();
+
+ let without_default = Some(Foo);
+ without_default.unwrap_or_else(Foo::new);
+
+ let mut map = HashMap::<u64, String>::new();
+ map.entry(42).or_insert(String::new());
+
+ let mut map_vec = HashMap::<u64, Vec<i32>>::new();
+ map_vec.entry(42).or_insert(vec![]);
+
+ let mut btree = BTreeMap::<u64, String>::new();
+ btree.entry(42).or_insert(String::new());
+
+ let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
+ btree_vec.entry(42).or_insert(vec![]);
+
+ let stringy = Some(String::from(""));
+ let _ = stringy.unwrap_or_else(|| "".to_owned());
+
+ let opt = Some(1);
+ let hello = "Hello";
+ let _ = opt.ok_or(format!("{} world.", hello));
+
+ // index
+ let map = HashMap::<u64, u64>::new();
+ let _ = Some(1).unwrap_or_else(|| map[&1]);
+ let map = BTreeMap::<u64, u64>::new();
+ let _ = Some(1).unwrap_or_else(|| map[&1]);
+ // don't lint index vec
+ let vec = vec![1];
+ let _ = Some(1).unwrap_or(vec[1]);
+}
+
+struct Foo(u8);
+struct Bar(String, Duration);
+#[rustfmt::skip]
+fn test_or_with_ctors() {
+ let opt = Some(1);
+ let opt_opt = Some(Some(1));
+ // we also test for const promotion, this makes sure we don't hit that
+ let two = 2;
+
+ let _ = opt_opt.unwrap_or(Some(2));
+ let _ = opt_opt.unwrap_or(Some(two));
+ let _ = opt.ok_or(Some(2));
+ let _ = opt.ok_or(Some(two));
+ let _ = opt.ok_or(Foo(2));
+ let _ = opt.ok_or(Foo(two));
+ let _ = opt.or(Some(2));
+ let _ = opt.or(Some(two));
+
+ let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
+
+ let b = "b".to_string();
+ let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
+ .or(Some(Bar(b, Duration::from_secs(2))));
+
+ let vec = vec!["foo"];
+ let _ = opt.ok_or(vec.len());
+
+ let array = ["foo"];
+ let _ = opt.ok_or(array.len());
+
+ let slice = &["foo"][..];
+ let _ = opt.ok_or(slice.len());
+
+ let string = "foo";
+ let _ = opt.ok_or(string.len());
+}
+
+// Issue 4514 - early return
+fn f() -> Option<()> {
+ let a = Some(1);
+ let b = 1i32;
+
+ let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
+
+ Some(())
+}
+
+mod issue6675 {
+ unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+ #[allow(unused)]
+ let x = vec![0; 1000]; // future-proofing, make this function expensive.
+ &*p
+ }
+
+ unsafe fn foo() {
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or_else(|| ptr_to_ref(s));
+ }
+
+ fn bar() {
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
+ #[rustfmt::skip]
+ None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
+ }
+}
+
+mod issue8239 {
+ fn more_than_max_suggestion_highest_lines_0() {
+ let frames = Vec::new();
+ frames
+ .iter()
+ .map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+
+ fn more_to_max_suggestion_highest_lines_1() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+
+ fn equal_to_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+
+ fn less_than_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ let map = iter.map(|f: &String| f.to_lowercase());
+ map.reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
new file mode 100644
index 000000000..57ab5f03e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -0,0 +1,229 @@
+// run-rustfix
+
+#![warn(clippy::or_fun_call)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::time::Duration;
+
+/// Checks implementation of the `OR_FUN_CALL` lint.
+fn or_fun_call() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo
+ }
+ }
+
+ struct FakeDefault;
+ impl FakeDefault {
+ fn default() -> Self {
+ FakeDefault
+ }
+ }
+
+ impl Default for FakeDefault {
+ fn default() -> Self {
+ FakeDefault
+ }
+ }
+
+ enum Enum {
+ A(i32),
+ }
+
+ fn make<T>() -> T {
+ unimplemented!();
+ }
+
+ let with_enum = Some(Enum::A(1));
+ with_enum.unwrap_or(Enum::A(5));
+
+ let with_const_fn = Some(Duration::from_secs(1));
+ with_const_fn.unwrap_or(Duration::from_secs(5));
+
+ let with_constructor = Some(vec![1]);
+ with_constructor.unwrap_or(make());
+
+ let with_new = Some(vec![1]);
+ with_new.unwrap_or(Vec::new());
+
+ let with_const_args = Some(vec![1]);
+ with_const_args.unwrap_or(Vec::with_capacity(12));
+
+ let with_err: Result<_, ()> = Ok(vec![1]);
+ with_err.unwrap_or(make());
+
+ let with_err_args: Result<_, ()> = Ok(vec![1]);
+ with_err_args.unwrap_or(Vec::with_capacity(12));
+
+ let with_default_trait = Some(1);
+ with_default_trait.unwrap_or(Default::default());
+
+ let with_default_type = Some(1);
+ with_default_type.unwrap_or(u64::default());
+
+ let self_default = None::<FakeDefault>;
+ self_default.unwrap_or(<FakeDefault>::default());
+
+ let real_default = None::<FakeDefault>;
+ real_default.unwrap_or(<FakeDefault as Default>::default());
+
+ let with_vec = Some(vec![1]);
+ with_vec.unwrap_or(vec![]);
+
+ let without_default = Some(Foo);
+ without_default.unwrap_or(Foo::new());
+
+ let mut map = HashMap::<u64, String>::new();
+ map.entry(42).or_insert(String::new());
+
+ let mut map_vec = HashMap::<u64, Vec<i32>>::new();
+ map_vec.entry(42).or_insert(vec![]);
+
+ let mut btree = BTreeMap::<u64, String>::new();
+ btree.entry(42).or_insert(String::new());
+
+ let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
+ btree_vec.entry(42).or_insert(vec![]);
+
+ let stringy = Some(String::from(""));
+ let _ = stringy.unwrap_or("".to_owned());
+
+ let opt = Some(1);
+ let hello = "Hello";
+ let _ = opt.ok_or(format!("{} world.", hello));
+
+ // index
+ let map = HashMap::<u64, u64>::new();
+ let _ = Some(1).unwrap_or(map[&1]);
+ let map = BTreeMap::<u64, u64>::new();
+ let _ = Some(1).unwrap_or(map[&1]);
+ // don't lint index vec
+ let vec = vec![1];
+ let _ = Some(1).unwrap_or(vec[1]);
+}
+
+struct Foo(u8);
+struct Bar(String, Duration);
+#[rustfmt::skip]
+fn test_or_with_ctors() {
+ let opt = Some(1);
+ let opt_opt = Some(Some(1));
+ // we also test for const promotion, this makes sure we don't hit that
+ let two = 2;
+
+ let _ = opt_opt.unwrap_or(Some(2));
+ let _ = opt_opt.unwrap_or(Some(two));
+ let _ = opt.ok_or(Some(2));
+ let _ = opt.ok_or(Some(two));
+ let _ = opt.ok_or(Foo(2));
+ let _ = opt.ok_or(Foo(two));
+ let _ = opt.or(Some(2));
+ let _ = opt.or(Some(two));
+
+ let _ = Some("a".to_string()).or(Some("b".to_string()));
+
+ let b = "b".to_string();
+ let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
+ .or(Some(Bar(b, Duration::from_secs(2))));
+
+ let vec = vec!["foo"];
+ let _ = opt.ok_or(vec.len());
+
+ let array = ["foo"];
+ let _ = opt.ok_or(array.len());
+
+ let slice = &["foo"][..];
+ let _ = opt.ok_or(slice.len());
+
+ let string = "foo";
+ let _ = opt.ok_or(string.len());
+}
+
+// Issue 4514 - early return
+fn f() -> Option<()> {
+ let a = Some(1);
+ let b = 1i32;
+
+ let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
+
+ Some(())
+}
+
+mod issue6675 {
+ unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+ #[allow(unused)]
+ let x = vec![0; 1000]; // future-proofing, make this function expensive.
+ &*p
+ }
+
+ unsafe fn foo() {
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or(ptr_to_ref(s));
+ }
+
+ fn bar() {
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or(unsafe { ptr_to_ref(s) });
+ #[rustfmt::skip]
+ None.unwrap_or( unsafe { ptr_to_ref(s) } );
+ }
+}
+
+mod issue8239 {
+ fn more_than_max_suggestion_highest_lines_0() {
+ let frames = Vec::new();
+ frames
+ .iter()
+ .map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn more_to_max_suggestion_highest_lines_1() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn equal_to_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn less_than_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ let map = iter.map(|f: &String| f.to_lowercase());
+ map.reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
new file mode 100644
index 000000000..4c5938ab8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -0,0 +1,136 @@
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:49:22
+ |
+LL | with_constructor.unwrap_or(make());
+ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
+ |
+ = note: `-D clippy::or-fun-call` implied by `-D warnings`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:52:14
+ |
+LL | with_new.unwrap_or(Vec::new());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:55:21
+ |
+LL | with_const_args.unwrap_or(Vec::with_capacity(12));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:58:14
+ |
+LL | with_err.unwrap_or(make());
+ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:61:19
+ |
+LL | with_err_args.unwrap_or(Vec::with_capacity(12));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
+
+error: use of `unwrap_or` followed by a call to `default`
+ --> $DIR/or_fun_call.rs:64:24
+ |
+LL | with_default_trait.unwrap_or(Default::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `default`
+ --> $DIR/or_fun_call.rs:67:23
+ |
+LL | with_default_type.unwrap_or(u64::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:70:18
+ |
+LL | self_default.unwrap_or(<FakeDefault>::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
+
+error: use of `unwrap_or` followed by a call to `default`
+ --> $DIR/or_fun_call.rs:73:18
+ |
+LL | real_default.unwrap_or(<FakeDefault as Default>::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:76:14
+ |
+LL | with_vec.unwrap_or(vec![]);
+ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:79:21
+ |
+LL | without_default.unwrap_or(Foo::new());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:94:21
+ |
+LL | let _ = stringy.unwrap_or("".to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:102:21
+ |
+LL | let _ = Some(1).unwrap_or(map[&1]);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:104:21
+ |
+LL | let _ = Some(1).unwrap_or(map[&1]);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
+
+error: use of `or` followed by a function call
+ --> $DIR/or_fun_call.rs:128:35
+ |
+LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:167:14
+ |
+LL | None.unwrap_or(ptr_to_ref(s));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:173:14
+ |
+LL | None.unwrap_or(unsafe { ptr_to_ref(s) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:175:14
+ |
+LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:189:14
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:202:14
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:214:14
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:225:10
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
new file mode 100644
index 000000000..844cc4b7a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![warn(clippy::or_then_unwrap)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
+
+struct SomeStruct;
+impl SomeStruct {
+ fn or(self, _: Option<Self>) -> Self {
+ self
+ }
+ fn unwrap(&self) {}
+}
+
+struct SomeOtherStruct;
+impl SomeOtherStruct {
+ fn or(self) -> Self {
+ self
+ }
+ fn unwrap(&self) {}
+}
+
+fn main() {
+ let option: Option<&str> = None;
+ let _ = option.unwrap_or("fallback"); // should trigger lint
+
+ let result: Result<&str, &str> = Err("Error");
+ let _ = result.unwrap_or("fallback"); // should trigger lint
+
+ // as part of a method chain
+ let option: Option<&str> = None;
+ let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint
+
+ // Not Option/Result
+ let instance = SomeStruct {};
+ let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
+
+ // or takes no argument
+ let instance = SomeOtherStruct {};
+ let _ = instance.or().unwrap(); // should not trigger lint and should not panic
+
+ // None in or
+ let option: Option<&str> = None;
+ let _ = option.or(None).unwrap(); // should not trigger lint
+
+ // Not Err in or
+ let result: Result<&str, &str> = Err("Error");
+ let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
+
+ // other function between
+ let option: Option<&str> = None;
+ let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs
new file mode 100644
index 000000000..1528ef9be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![warn(clippy::or_then_unwrap)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
+
+struct SomeStruct;
+impl SomeStruct {
+ fn or(self, _: Option<Self>) -> Self {
+ self
+ }
+ fn unwrap(&self) {}
+}
+
+struct SomeOtherStruct;
+impl SomeOtherStruct {
+ fn or(self) -> Self {
+ self
+ }
+ fn unwrap(&self) {}
+}
+
+fn main() {
+ let option: Option<&str> = None;
+ let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
+
+ let result: Result<&str, &str> = Err("Error");
+ let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
+
+ // as part of a method chain
+ let option: Option<&str> = None;
+ let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
+
+ // Not Option/Result
+ let instance = SomeStruct {};
+ let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
+
+ // or takes no argument
+ let instance = SomeOtherStruct {};
+ let _ = instance.or().unwrap(); // should not trigger lint and should not panic
+
+ // None in or
+ let option: Option<&str> = None;
+ let _ = option.or(None).unwrap(); // should not trigger lint
+
+ // Not Err in or
+ let result: Result<&str, &str> = Err("Error");
+ let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
+
+ // other function between
+ let option: Option<&str> = None;
+ let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.stderr b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
new file mode 100644
index 000000000..da88154c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
@@ -0,0 +1,22 @@
+error: found `.or(Some(…)).unwrap()`
+ --> $DIR/or_then_unwrap.rs:24:20
+ |
+LL | let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+ |
+ = note: `-D clippy::or-then-unwrap` implied by `-D warnings`
+
+error: found `.or(Ok(…)).unwrap()`
+ --> $DIR/or_then_unwrap.rs:27:20
+ |
+LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+
+error: found `.or(Some(…)).unwrap()`
+ --> $DIR/or_then_unwrap.rs:31:31
+ |
+LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs
new file mode 100644
index 000000000..f20a0ede1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs
@@ -0,0 +1,11 @@
+#![warn(clippy::out_of_bounds_indexing)]
+#![allow(clippy::no_effect, const_err)]
+
+fn main() {
+ let x = [1, 2, 3, 4];
+
+ // issue 3102
+ let num = 1;
+ &x[num..10]; // should trigger out of bounds error
+ &x[10..num]; // should trigger out of bounds error
+}
diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr
new file mode 100644
index 000000000..516c1df40
--- /dev/null
+++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr
@@ -0,0 +1,16 @@
+error: range is out of bounds
+ --> $DIR/issue-3102.rs:9:13
+ |
+LL | &x[num..10]; // should trigger out of bounds error
+ | ^^
+ |
+ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
+
+error: range is out of bounds
+ --> $DIR/issue-3102.rs:10:8
+ |
+LL | &x[10..num]; // should trigger out of bounds error
+ | ^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs
new file mode 100644
index 000000000..590e578d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::out_of_bounds_indexing)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)]
+
+fn main() {
+ let x = [1, 2, 3, 4];
+
+ &x[..=4];
+ &x[1..5];
+ &x[5..];
+ &x[..5];
+ &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
+ &x[0..=4];
+
+ &x[4..]; // Ok, should not produce stderr.
+ &x[..4]; // Ok, should not produce stderr.
+ &x[..]; // Ok, should not produce stderr.
+ &x[1..]; // Ok, should not produce stderr.
+ &x[2..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); // Ok, should not produce stderr.
+
+ &x[0..].get(..3); // Ok, should not produce stderr.
+ &x[0..3]; // Ok, should not produce stderr.
+}
diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr
new file mode 100644
index 000000000..3d95afcda
--- /dev/null
+++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr
@@ -0,0 +1,40 @@
+error: range is out of bounds
+ --> $DIR/simple.rs:7:11
+ |
+LL | &x[..=4];
+ | ^
+ |
+ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
+
+error: range is out of bounds
+ --> $DIR/simple.rs:8:11
+ |
+LL | &x[1..5];
+ | ^
+
+error: range is out of bounds
+ --> $DIR/simple.rs:9:8
+ |
+LL | &x[5..];
+ | ^
+
+error: range is out of bounds
+ --> $DIR/simple.rs:10:10
+ |
+LL | &x[..5];
+ | ^
+
+error: range is out of bounds
+ --> $DIR/simple.rs:11:8
+ |
+LL | &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
+ | ^
+
+error: range is out of bounds
+ --> $DIR/simple.rs:12:12
+ |
+LL | &x[0..=4];
+ | ^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
new file mode 100644
index 000000000..5db75f529
--- /dev/null
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
@@ -0,0 +1,25 @@
+#![warn(clippy::overflow_check_conditional)]
+
+fn main() {
+ let a: u32 = 1;
+ let b: u32 = 2;
+ let c: u32 = 3;
+ if a + b < a {}
+ if a > a + b {}
+ if a + b < b {}
+ if b > a + b {}
+ if a - b > b {}
+ if b < a - b {}
+ if a - b > a {}
+ if a < a - b {}
+ if a + b < c {}
+ if c > a + b {}
+ if a - b < c {}
+ if c > a - b {}
+ let i = 1.1;
+ let j = 2.2;
+ if i + j < i {}
+ if i - j < i {}
+ if i > i + j {}
+ if i - j < i {}
+}
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
new file mode 100644
index 000000000..1b8b146b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
@@ -0,0 +1,52 @@
+error: you are trying to use classic C overflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:7:8
+ |
+LL | if a + b < a {}
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:8:8
+ |
+LL | if a > a + b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:9:8
+ |
+LL | if a + b < b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:10:8
+ |
+LL | if b > a + b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:11:8
+ |
+LL | if a - b > b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:12:8
+ |
+LL | if b < a - b {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:13:8
+ |
+LL | if a - b > a {}
+ | ^^^^^^^^^
+
+error: you are trying to use classic C underflow conditions that will fail in Rust
+ --> $DIR/overflow_check_conditional.rs:14:8
+ |
+LL | if a < a - b {}
+ | ^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.rs b/src/tools/clippy/tests/ui/panic_in_result_fn.rs
new file mode 100644
index 000000000..e75eb1b6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn.rs
@@ -0,0 +1,70 @@
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+struct A;
+
+impl A {
+ fn result_with_panic() -> Result<bool, String> // should emit lint
+ {
+ panic!("error");
+ }
+
+ fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+ {
+ unimplemented!();
+ }
+
+ fn result_with_unreachable() -> Result<bool, String> // should emit lint
+ {
+ unreachable!();
+ }
+
+ fn result_with_todo() -> Result<bool, String> // should emit lint
+ {
+ todo!("Finish this");
+ }
+
+ fn other_with_panic() // should not emit lint
+ {
+ panic!("");
+ }
+
+ fn other_with_unreachable() // should not emit lint
+ {
+ unreachable!();
+ }
+
+ fn other_with_unimplemented() // should not emit lint
+ {
+ unimplemented!();
+ }
+
+ fn other_with_todo() // should not emit lint
+ {
+ todo!("finish this")
+ }
+
+ fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+ {
+ Ok(true)
+ }
+}
+
+fn function_result_with_panic() -> Result<bool, String> // should emit lint
+{
+ panic!("error");
+}
+
+fn todo() {
+ println!("something");
+}
+
+fn function_result_with_custom_todo() -> Result<bool, String> // should not emit lint
+{
+ todo();
+ Ok(true)
+}
+
+fn main() -> Result<(), String> {
+ todo!("finish main method");
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr
new file mode 100644
index 000000000..561503ae5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr
@@ -0,0 +1,99 @@
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:6:5
+ |
+LL | / fn result_with_panic() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | panic!("error");
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:8:9
+ |
+LL | panic!("error");
+ | ^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:11:5
+ |
+LL | / fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | unimplemented!();
+LL | | }
+ | |_____^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:13:9
+ |
+LL | unimplemented!();
+ | ^^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:16:5
+ |
+LL | / fn result_with_unreachable() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | unreachable!();
+LL | | }
+ | |_____^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:18:9
+ |
+LL | unreachable!();
+ | ^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:21:5
+ |
+LL | / fn result_with_todo() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | todo!("Finish this");
+LL | | }
+ | |_____^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:23:9
+ |
+LL | todo!("Finish this");
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:52:1
+ |
+LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
+LL | | {
+LL | | panic!("error");
+LL | | }
+ | |_^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:54:5
+ |
+LL | panic!("error");
+ | ^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn.rs:67:1
+ |
+LL | / fn main() -> Result<(), String> {
+LL | | todo!("finish main method");
+LL | | Ok(())
+LL | | }
+ | |_^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn.rs:68:5
+ |
+LL | todo!("finish main method");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs
new file mode 100644
index 000000000..ffdf8288a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs
@@ -0,0 +1,48 @@
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+
+struct A;
+
+impl A {
+ fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+ {
+ assert!(x == 5, "wrong argument");
+ Ok(true)
+ }
+
+ fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+ {
+ assert_eq!(x, 5);
+ Ok(true)
+ }
+
+ fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+ {
+ assert_ne!(x, 1);
+ Ok(true)
+ }
+
+ fn other_with_assert_with_message(x: i32) // should not emit lint
+ {
+ assert!(x == 5, "wrong argument");
+ }
+
+ fn other_with_assert_eq(x: i32) // should not emit lint
+ {
+ assert_eq!(x, 5);
+ }
+
+ fn other_with_assert_ne(x: i32) // should not emit lint
+ {
+ assert_ne!(x, 1);
+ }
+
+ fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+ {
+ let assert = "assert!";
+ println!("No {}", assert);
+ Ok(true)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr
new file mode 100644
index 000000000..b6aa005e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr
@@ -0,0 +1,54 @@
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn_assertions.rs:7:5
+ |
+LL | / fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+LL | | {
+LL | | assert!(x == 5, "wrong argument");
+LL | | Ok(true)
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn_assertions.rs:9:9
+ |
+LL | assert!(x == 5, "wrong argument");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn_assertions.rs:13:5
+ |
+LL | / fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+LL | | {
+LL | | assert_eq!(x, 5);
+LL | | Ok(true)
+LL | | }
+ | |_____^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn_assertions.rs:15:9
+ |
+LL | assert_eq!(x, 5);
+ | ^^^^^^^^^^^^^^^^
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+ --> $DIR/panic_in_result_fn_assertions.rs:19:5
+ |
+LL | / fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+LL | | {
+LL | | assert_ne!(x, 1);
+LL | | Ok(true)
+LL | | }
+ | |_____^
+ |
+ = 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
+note: return Err() instead of panicking
+ --> $DIR/panic_in_result_fn_assertions.rs:21:9
+ |
+LL | assert_ne!(x, 1);
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
new file mode 100644
index 000000000..c4fcd7e70
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
@@ -0,0 +1,43 @@
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+
+// debug_assert should never trigger the `panic_in_result_fn` lint
+
+struct A;
+
+impl A {
+ fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> {
+ debug_assert!(x == 5, "wrong argument");
+ Ok(true)
+ }
+
+ fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> {
+ debug_assert_eq!(x, 5);
+ Ok(true)
+ }
+
+ fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> {
+ debug_assert_ne!(x, 1);
+ Ok(true)
+ }
+
+ fn other_with_debug_assert_with_message(x: i32) {
+ debug_assert!(x == 5, "wrong argument");
+ }
+
+ fn other_with_debug_assert_eq(x: i32) {
+ debug_assert_eq!(x, 5);
+ }
+
+ fn other_with_debug_assert_ne(x: i32) {
+ debug_assert_ne!(x, 1);
+ }
+
+ fn result_without_banned_functions() -> Result<bool, String> {
+ let debug_assert = "debug_assert!";
+ println!("No {}", debug_assert);
+ Ok(true)
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs
new file mode 100644
index 000000000..041ef17fa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panicking_macros.rs
@@ -0,0 +1,95 @@
+#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
+#![feature(inline_const)]
+#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
+
+extern crate core;
+
+const _: () = {
+ if 1 == 0 {
+ panic!("A balanced diet means a cupcake in each hand");
+ }
+};
+
+fn inline_const() {
+ let _ = const {
+ if 1 == 0 {
+ panic!("When nothing goes right, go left")
+ }
+ };
+}
+
+fn panic() {
+ let a = 2;
+ panic!();
+ panic!("message");
+ panic!("{} {}", "panic with", "multiple arguments");
+ let b = a + 2;
+}
+
+fn todo() {
+ let a = 2;
+ todo!();
+ todo!("message");
+ todo!("{} {}", "panic with", "multiple arguments");
+ let b = a + 2;
+}
+
+fn unimplemented() {
+ let a = 2;
+ unimplemented!();
+ unimplemented!("message");
+ unimplemented!("{} {}", "panic with", "multiple arguments");
+ let b = a + 2;
+}
+
+fn unreachable() {
+ let a = 2;
+ unreachable!();
+ unreachable!("message");
+ unreachable!("{} {}", "panic with", "multiple arguments");
+ let b = a + 2;
+}
+
+fn core_versions() {
+ use core::{panic, todo, unimplemented, unreachable};
+ panic!();
+ todo!();
+ unimplemented!();
+ unreachable!();
+}
+
+fn assert() {
+ assert!(true);
+ assert_eq!(true, true);
+ assert_ne!(true, false);
+}
+
+fn assert_msg() {
+ assert!(true, "this should not panic");
+ assert_eq!(true, true, "this should not panic");
+ assert_ne!(true, false, "this should not panic");
+}
+
+fn debug_assert() {
+ debug_assert!(true);
+ debug_assert_eq!(true, true);
+ debug_assert_ne!(true, false);
+}
+
+fn debug_assert_msg() {
+ debug_assert!(true, "test");
+ debug_assert_eq!(true, true, "test");
+ debug_assert_ne!(true, false, "test");
+}
+
+fn main() {
+ panic();
+ todo();
+ unimplemented();
+ unreachable();
+ core_versions();
+ assert();
+ assert_msg();
+ debug_assert();
+ debug_assert_msg();
+}
diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr
new file mode 100644
index 000000000..4ceb6d144
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panicking_macros.stderr
@@ -0,0 +1,106 @@
+error: `panic` should not be present in production code
+ --> $DIR/panicking_macros.rs:23:5
+ |
+LL | panic!();
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::panic` implied by `-D warnings`
+
+error: `panic` should not be present in production code
+ --> $DIR/panicking_macros.rs:24:5
+ |
+LL | panic!("message");
+ | ^^^^^^^^^^^^^^^^^
+
+error: `panic` should not be present in production code
+ --> $DIR/panicking_macros.rs:25:5
+ |
+LL | panic!("{} {}", "panic with", "multiple arguments");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `todo` should not be present in production code
+ --> $DIR/panicking_macros.rs:31:5
+ |
+LL | todo!();
+ | ^^^^^^^
+ |
+ = note: `-D clippy::todo` implied by `-D warnings`
+
+error: `todo` should not be present in production code
+ --> $DIR/panicking_macros.rs:32:5
+ |
+LL | todo!("message");
+ | ^^^^^^^^^^^^^^^^
+
+error: `todo` should not be present in production code
+ --> $DIR/panicking_macros.rs:33:5
+ |
+LL | todo!("{} {}", "panic with", "multiple arguments");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `unimplemented` should not be present in production code
+ --> $DIR/panicking_macros.rs:39:5
+ |
+LL | unimplemented!();
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unimplemented` implied by `-D warnings`
+
+error: `unimplemented` should not be present in production code
+ --> $DIR/panicking_macros.rs:40:5
+ |
+LL | unimplemented!("message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `unimplemented` should not be present in production code
+ --> $DIR/panicking_macros.rs:41:5
+ |
+LL | unimplemented!("{} {}", "panic with", "multiple arguments");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: usage of the `unreachable!` macro
+ --> $DIR/panicking_macros.rs:47:5
+ |
+LL | unreachable!();
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unreachable` implied by `-D warnings`
+
+error: usage of the `unreachable!` macro
+ --> $DIR/panicking_macros.rs:48:5
+ |
+LL | unreachable!("message");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: usage of the `unreachable!` macro
+ --> $DIR/panicking_macros.rs:49:5
+ |
+LL | unreachable!("{} {}", "panic with", "multiple arguments");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `panic` should not be present in production code
+ --> $DIR/panicking_macros.rs:55:5
+ |
+LL | panic!();
+ | ^^^^^^^^
+
+error: `todo` should not be present in production code
+ --> $DIR/panicking_macros.rs:56:5
+ |
+LL | todo!();
+ | ^^^^^^^
+
+error: `unimplemented` should not be present in production code
+ --> $DIR/panicking_macros.rs:57:5
+ |
+LL | unimplemented!();
+ | ^^^^^^^^^^^^^^^^
+
+error: usage of the `unreachable!` macro
+ --> $DIR/panicking_macros.rs:58:5
+ |
+LL | unreachable!();
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.rs b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs
new file mode 100644
index 000000000..1338d3c74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs
@@ -0,0 +1,26 @@
+#![allow(dead_code)]
+
+struct Foo;
+
+impl PartialEq for Foo {
+ fn eq(&self, _: &Foo) -> bool {
+ true
+ }
+ fn ne(&self, _: &Foo) -> bool {
+ false
+ }
+}
+
+struct Bar;
+
+impl PartialEq for Bar {
+ fn eq(&self, _: &Bar) -> bool {
+ true
+ }
+ #[allow(clippy::partialeq_ne_impl)]
+ fn ne(&self, _: &Bar) -> bool {
+ false
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
new file mode 100644
index 000000000..b92da4511
--- /dev/null
+++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
@@ -0,0 +1,12 @@
+error: re-implementing `PartialEq::ne` is unnecessary
+ --> $DIR/partialeq_ne_impl.rs:9:5
+ |
+LL | / fn ne(&self, _: &Foo) -> bool {
+LL | | false
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed
new file mode 100644
index 000000000..ef8856830
--- /dev/null
+++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+use std::path::PathBuf;
+
+#[warn(clippy::all, clippy::path_buf_push_overwrite)]
+fn main() {
+ let mut x = PathBuf::from("/foo");
+ x.push("bar");
+}
diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs
new file mode 100644
index 000000000..6e2d483f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+use std::path::PathBuf;
+
+#[warn(clippy::all, clippy::path_buf_push_overwrite)]
+fn main() {
+ let mut x = PathBuf::from("/foo");
+ x.push("/bar");
+}
diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr
new file mode 100644
index 000000000..bb8dce2bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr
@@ -0,0 +1,10 @@
+error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
+ --> $DIR/path_buf_push_overwrite.rs:7:12
+ |
+LL | x.push("/bar");
+ | ^^^^^^ help: try: `"bar"`
+ |
+ = note: `-D clippy::path-buf-push-overwrite` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
new file mode 100644
index 000000000..55a8c2621
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
@@ -0,0 +1,49 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn should_lint() {
+ let value = &Some(23);
+ match value {
+ Some(_) => (),
+ _ => (),
+ }
+
+ let value = &mut Some(23);
+ match value {
+ Some(_) => (),
+ _ => (),
+ }
+}
+
+fn should_not_lint() {
+ let value = &Some(23);
+ match value {
+ &Some(_) => (),
+ _ => (),
+ }
+ match *value {
+ Some(_) => (),
+ _ => (),
+ }
+
+ let value = &mut Some(23);
+ match value {
+ &mut Some(_) => (),
+ _ => (),
+ }
+ match *value {
+ Some(_) => (),
+ _ => (),
+ }
+
+ const FOO: &str = "foo";
+
+ fn foo(s: &str) -> i32 {
+ match s {
+ FOO => 1,
+ _ => 0,
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr
new file mode 100644
index 000000000..3421d5683
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr
@@ -0,0 +1,19 @@
+error: type of pattern does not match the expression type
+ --> $DIR/mutability.rs:9:9
+ |
+LL | Some(_) => (),
+ | ^^^^^^^
+ |
+ = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/mutability.rs:15:9
+ |
+LL | Some(_) => (),
+ | ^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
new file mode 100644
index 000000000..065ea9fb9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
@@ -0,0 +1,24 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn alternatives() {
+ enum Value<'a> {
+ Unused,
+ A(&'a Option<i32>),
+ B,
+ }
+ let ref_value = &Value::A(&Some(23));
+
+ // not ok
+ if let Value::B | Value::A(_) = ref_value {}
+ if let &Value::B | &Value::A(Some(_)) = ref_value {}
+ if let Value::B | Value::A(Some(_)) = *ref_value {}
+
+ // ok
+ if let &Value::B | &Value::A(_) = ref_value {}
+ if let Value::B | Value::A(_) = *ref_value {}
+ if let &Value::B | &Value::A(&Some(_)) = ref_value {}
+ if let Value::B | Value::A(&Some(_)) = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
new file mode 100644
index 000000000..d285c9378
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
@@ -0,0 +1,27 @@
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_alternatives.rs:15:12
+ |
+LL | if let Value::B | Value::A(_) = ref_value {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_alternatives.rs:16:34
+ |
+LL | if let &Value::B | &Value::A(Some(_)) = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_alternatives.rs:17:32
+ |
+LL | if let Value::B | Value::A(Some(_)) = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs
new file mode 100644
index 000000000..417b1c107
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs
@@ -0,0 +1,45 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn struct_types() {
+ struct Struct<'a> {
+ ref_inner: &'a Option<i32>,
+ }
+ let ref_value = &Struct { ref_inner: &Some(42) };
+
+ // not ok
+ let Struct { .. } = ref_value;
+ if let &Struct { ref_inner: Some(_) } = ref_value {}
+ if let Struct { ref_inner: Some(_) } = *ref_value {}
+
+ // ok
+ let &Struct { .. } = ref_value;
+ let Struct { .. } = *ref_value;
+ if let &Struct { ref_inner: &Some(_) } = ref_value {}
+ if let Struct { ref_inner: &Some(_) } = *ref_value {}
+}
+
+fn struct_enum_variants() {
+ enum StructEnum<'a> {
+ Empty,
+ Var { inner_ref: &'a Option<i32> },
+ }
+ let ref_value = &StructEnum::Var { inner_ref: &Some(42) };
+
+ // not ok
+ if let StructEnum::Var { .. } = ref_value {}
+ if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+ if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+ if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+ if let StructEnum::Empty = ref_value {}
+
+ // ok
+ if let &StructEnum::Var { .. } = ref_value {}
+ if let StructEnum::Var { .. } = *ref_value {}
+ if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {}
+ if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {}
+ if let &StructEnum::Empty = ref_value {}
+ if let StructEnum::Empty = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr
new file mode 100644
index 000000000..d428e85b0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr
@@ -0,0 +1,67 @@
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:13:9
+ |
+LL | let Struct { .. } = ref_value;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:14:33
+ |
+LL | if let &Struct { ref_inner: Some(_) } = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:15:32
+ |
+LL | if let Struct { ref_inner: Some(_) } = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:32:12
+ |
+LL | if let StructEnum::Var { .. } = ref_value {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:33:12
+ |
+LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:34:42
+ |
+LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:35:41
+ |
+LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_structs.rs:36:12
+ |
+LL | if let StructEnum::Empty = ref_value {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs
new file mode 100644
index 000000000..19504a051
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs
@@ -0,0 +1,57 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn tuple_types() {
+ struct TupleStruct<'a>(&'a Option<i32>);
+ let ref_value = &TupleStruct(&Some(42));
+
+ // not ok
+ let TupleStruct(_) = ref_value;
+ if let &TupleStruct(Some(_)) = ref_value {}
+ if let TupleStruct(Some(_)) = *ref_value {}
+
+ // ok
+ let &TupleStruct(_) = ref_value;
+ let TupleStruct(_) = *ref_value;
+ if let &TupleStruct(&Some(_)) = ref_value {}
+ if let TupleStruct(&Some(_)) = *ref_value {}
+}
+
+fn tuple_enum_variants() {
+ enum TupleEnum<'a> {
+ Empty,
+ Var(&'a Option<i32>),
+ }
+ let ref_value = &TupleEnum::Var(&Some(42));
+
+ // not ok
+ if let TupleEnum::Var(_) = ref_value {}
+ if let &TupleEnum::Var(Some(_)) = ref_value {}
+ if let TupleEnum::Var(Some(_)) = *ref_value {}
+ if let TupleEnum::Empty = ref_value {}
+
+ // ok
+ if let &TupleEnum::Var(_) = ref_value {}
+ if let TupleEnum::Var(_) = *ref_value {}
+ if let &TupleEnum::Var(&Some(_)) = ref_value {}
+ if let TupleEnum::Var(&Some(_)) = *ref_value {}
+ if let &TupleEnum::Empty = ref_value {}
+ if let TupleEnum::Empty = *ref_value {}
+}
+
+fn plain_tuples() {
+ let ref_value = &(&Some(23), &Some(42));
+
+ // not ok
+ let (_a, _b) = ref_value;
+ if let &(_a, Some(_)) = ref_value {}
+ if let (_a, Some(_)) = *ref_value {}
+
+ // ok
+ let &(_a, _b) = ref_value;
+ let (_a, _b) = *ref_value;
+ if let &(_a, &Some(_)) = ref_value {}
+ if let (_a, &Some(_)) = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
new file mode 100644
index 000000000..edd0074d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
@@ -0,0 +1,83 @@
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:11:9
+ |
+LL | let TupleStruct(_) = ref_value;
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:12:25
+ |
+LL | if let &TupleStruct(Some(_)) = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:13:24
+ |
+LL | if let TupleStruct(Some(_)) = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:30:12
+ |
+LL | if let TupleEnum::Var(_) = ref_value {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:31:28
+ |
+LL | if let &TupleEnum::Var(Some(_)) = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:32:27
+ |
+LL | if let TupleEnum::Var(Some(_)) = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:33:12
+ |
+LL | if let TupleEnum::Empty = ref_value {}
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:48:9
+ |
+LL | let (_a, _b) = ref_value;
+ | ^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:49:18
+ |
+LL | if let &(_a, Some(_)) = ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/pattern_tuples.rs:50:17
+ |
+LL | if let (_a, Some(_)) = *ref_value {}
+ | ^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs
new file mode 100644
index 000000000..e89917c41
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs
@@ -0,0 +1,146 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn syntax_match() {
+ let ref_value = &Some(&Some(42));
+
+ // not ok
+ match ref_value {
+ Some(_) => (),
+ None => (),
+ }
+
+ // ok
+ match ref_value {
+ &Some(_) => (),
+ &None => (),
+ }
+ match *ref_value {
+ Some(_) => (),
+ None => (),
+ }
+}
+
+fn syntax_if_let() {
+ let ref_value = &Some(42);
+
+ // not ok
+ if let Some(_) = ref_value {}
+
+ // ok
+ if let &Some(_) = ref_value {}
+ if let Some(_) = *ref_value {}
+}
+
+fn syntax_while_let() {
+ let ref_value = &Some(42);
+
+ // not ok
+ while let Some(_) = ref_value {
+ break;
+ }
+
+ // ok
+ while let &Some(_) = ref_value {
+ break;
+ }
+ while let Some(_) = *ref_value {
+ break;
+ }
+}
+
+fn syntax_for() {
+ let ref_value = &Some(23);
+ let slice = &[(2, 3), (4, 2)];
+
+ // not ok
+ for (_a, _b) in slice.iter() {}
+
+ // ok
+ for &(_a, _b) in slice.iter() {}
+}
+
+fn syntax_let() {
+ let ref_value = &(2, 3);
+
+ // not ok
+ let (_n, _m) = ref_value;
+
+ // ok
+ let &(_n, _m) = ref_value;
+ let (_n, _m) = *ref_value;
+}
+
+fn syntax_fn() {
+ // not ok
+ fn foo((_a, _b): &(i32, i32)) {}
+
+ // ok
+ fn foo_ok_1(&(_a, _b): &(i32, i32)) {}
+}
+
+fn syntax_closure() {
+ fn foo<F>(f: F)
+ where
+ F: FnOnce(&(i32, i32)),
+ {
+ }
+
+ // not ok
+ foo(|(_a, _b)| ());
+
+ // ok
+ foo(|&(_a, _b)| ());
+}
+
+fn macro_with_expression() {
+ macro_rules! matching_macro {
+ ($e:expr) => {
+ $e
+ };
+ }
+ let value = &Some(23);
+
+ // not ok
+ matching_macro!(match value {
+ Some(_) => (),
+ _ => (),
+ });
+
+ // ok
+ matching_macro!(match value {
+ &Some(_) => (),
+ _ => (),
+ });
+ matching_macro!(match *value {
+ Some(_) => (),
+ _ => (),
+ });
+}
+
+fn macro_expansion() {
+ macro_rules! matching_macro {
+ ($e:expr) => {
+ // not ok
+ match $e {
+ Some(_) => (),
+ _ => (),
+ }
+
+ // ok
+ match $e {
+ &Some(_) => (),
+ _ => (),
+ }
+ match *$e {
+ Some(_) => (),
+ _ => (),
+ }
+ };
+ }
+
+ let value = &Some(23);
+ matching_macro!(value);
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr
new file mode 100644
index 000000000..12b3d3a8b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr
@@ -0,0 +1,79 @@
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:11:9
+ |
+LL | Some(_) => (),
+ | ^^^^^^^
+ |
+ = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:30:12
+ |
+LL | if let Some(_) = ref_value {}
+ | ^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:41:15
+ |
+LL | while let Some(_) = ref_value {
+ | ^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:59:9
+ |
+LL | for (_a, _b) in slice.iter() {}
+ | ^^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:69:9
+ |
+LL | let (_n, _m) = ref_value;
+ | ^^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:78:12
+ |
+LL | fn foo((_a, _b): &(i32, i32)) {}
+ | ^^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:92:10
+ |
+LL | foo(|(_a, _b)| ());
+ | ^^^^^^^^
+ |
+ = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:108:9
+ |
+LL | Some(_) => (),
+ | ^^^^^^^
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+ --> $DIR/syntax.rs:128:17
+ |
+LL | Some(_) => (),
+ | ^^^^^^^
+...
+LL | matching_macro!(value);
+ | ---------------------- in this macro invocation
+ |
+ = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+ = note: this error originates in the macro `matching_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed
new file mode 100644
index 000000000..f22388154
--- /dev/null
+++ b/src/tools/clippy/tests/ui/patterns.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::all)]
+
+fn main() {
+ let v = Some(true);
+ let s = [0, 1, 2, 3, 4];
+ match v {
+ Some(x) => (),
+ y => (),
+ }
+ match v {
+ Some(x) => (),
+ y @ None => (), // no error
+ }
+ match s {
+ [x, inside @ .., y] => (), // no error
+ [..] => (),
+ }
+
+ let mut mutv = vec![1, 2, 3];
+
+ // required "ref" left out in suggestion: #5271
+ match mutv {
+ ref mut x => {
+ x.push(4);
+ println!("vec: {:?}", x);
+ },
+ ref y if y == &vec![0] => (),
+ }
+
+ match mutv {
+ ref x => println!("vec: {:?}", x),
+ ref y if y == &vec![0] => (),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs
new file mode 100644
index 000000000..5848ecd38
--- /dev/null
+++ b/src/tools/clippy/tests/ui/patterns.rs
@@ -0,0 +1,36 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::all)]
+
+fn main() {
+ let v = Some(true);
+ let s = [0, 1, 2, 3, 4];
+ match v {
+ Some(x) => (),
+ y @ _ => (),
+ }
+ match v {
+ Some(x) => (),
+ y @ None => (), // no error
+ }
+ match s {
+ [x, inside @ .., y] => (), // no error
+ [..] => (),
+ }
+
+ let mut mutv = vec![1, 2, 3];
+
+ // required "ref" left out in suggestion: #5271
+ match mutv {
+ ref mut x @ _ => {
+ x.push(4);
+ println!("vec: {:?}", x);
+ },
+ ref y if y == &vec![0] => (),
+ }
+
+ match mutv {
+ ref x @ _ => println!("vec: {:?}", x),
+ ref y if y == &vec![0] => (),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr
new file mode 100644
index 000000000..af0675806
--- /dev/null
+++ b/src/tools/clippy/tests/ui/patterns.stderr
@@ -0,0 +1,22 @@
+error: the `y @ _` pattern can be written as just `y`
+ --> $DIR/patterns.rs:10:9
+ |
+LL | y @ _ => (),
+ | ^^^^^ help: try: `y`
+ |
+ = note: `-D clippy::redundant-pattern` implied by `-D warnings`
+
+error: the `x @ _` pattern can be written as just `x`
+ --> $DIR/patterns.rs:25:9
+ |
+LL | ref mut x @ _ => {
+ | ^^^^^^^^^^^^^ help: try: `ref mut x`
+
+error: the `x @ _` pattern can be written as just `x`
+ --> $DIR/patterns.rs:33:9
+ |
+LL | ref x @ _ => println!("vec: {:?}", x),
+ | ^^^^^^^^^ help: try: `ref x`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed
new file mode 100644
index 000000000..163bd044c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/precedence.fixed
@@ -0,0 +1,61 @@
+// run-rustfix
+#![warn(clippy::precedence)]
+#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+macro_rules! trip {
+ ($a:expr) => {
+ match $a & 0b1111_1111u8 {
+ 0 => println!("a is zero ({})", $a),
+ _ => println!("a is {}", $a),
+ }
+ };
+}
+
+fn main() {
+ 1 << (2 + 3);
+ (1 + 2) << 3;
+ 4 >> (1 + 1);
+ (1 + 3) >> 2;
+ 1 ^ (1 - 1);
+ 3 | (2 - 1);
+ 3 & (5 - 2);
+ -(1i32.abs());
+ -(1f32.abs());
+
+ // These should not trigger an error
+ let _ = (-1i32).abs();
+ let _ = (-1f32).abs();
+ let _ = -(1i32).abs();
+ let _ = -(1f32).abs();
+ let _ = -(1i32.abs());
+ let _ = -(1f32.abs());
+
+ // Odd functions should not trigger an error
+ let _ = -1f64.asin();
+ let _ = -1f64.asinh();
+ let _ = -1f64.atan();
+ let _ = -1f64.atanh();
+ let _ = -1f64.cbrt();
+ let _ = -1f64.fract();
+ let _ = -1f64.round();
+ let _ = -1f64.signum();
+ let _ = -1f64.sin();
+ let _ = -1f64.sinh();
+ let _ = -1f64.tan();
+ let _ = -1f64.tanh();
+ let _ = -1f64.to_degrees();
+ let _ = -1f64.to_radians();
+
+ // Chains containing any non-odd function should trigger (issue #5924)
+ let _ = -(1.0_f64.cos().cos());
+ let _ = -(1.0_f64.cos().sin());
+ let _ = -(1.0_f64.sin().cos());
+
+ // Chains of odd functions shouldn't trigger
+ let _ = -1f64.sin().sin();
+
+ let b = 3;
+ trip!(b * 8);
+}
diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs
new file mode 100644
index 000000000..8c849e320
--- /dev/null
+++ b/src/tools/clippy/tests/ui/precedence.rs
@@ -0,0 +1,61 @@
+// run-rustfix
+#![warn(clippy::precedence)]
+#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::eq_op)]
+
+macro_rules! trip {
+ ($a:expr) => {
+ match $a & 0b1111_1111u8 {
+ 0 => println!("a is zero ({})", $a),
+ _ => println!("a is {}", $a),
+ }
+ };
+}
+
+fn main() {
+ 1 << 2 + 3;
+ 1 + 2 << 3;
+ 4 >> 1 + 1;
+ 1 + 3 >> 2;
+ 1 ^ 1 - 1;
+ 3 | 2 - 1;
+ 3 & 5 - 2;
+ -1i32.abs();
+ -1f32.abs();
+
+ // These should not trigger an error
+ let _ = (-1i32).abs();
+ let _ = (-1f32).abs();
+ let _ = -(1i32).abs();
+ let _ = -(1f32).abs();
+ let _ = -(1i32.abs());
+ let _ = -(1f32.abs());
+
+ // Odd functions should not trigger an error
+ let _ = -1f64.asin();
+ let _ = -1f64.asinh();
+ let _ = -1f64.atan();
+ let _ = -1f64.atanh();
+ let _ = -1f64.cbrt();
+ let _ = -1f64.fract();
+ let _ = -1f64.round();
+ let _ = -1f64.signum();
+ let _ = -1f64.sin();
+ let _ = -1f64.sinh();
+ let _ = -1f64.tan();
+ let _ = -1f64.tanh();
+ let _ = -1f64.to_degrees();
+ let _ = -1f64.to_radians();
+
+ // Chains containing any non-odd function should trigger (issue #5924)
+ let _ = -1.0_f64.cos().cos();
+ let _ = -1.0_f64.cos().sin();
+ let _ = -1.0_f64.sin().cos();
+
+ // Chains of odd functions shouldn't trigger
+ let _ = -1f64.sin().sin();
+
+ let b = 3;
+ trip!(b * 8);
+}
diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr
new file mode 100644
index 000000000..03d585b39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/precedence.stderr
@@ -0,0 +1,76 @@
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:17:5
+ |
+LL | 1 << 2 + 3;
+ | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)`
+ |
+ = note: `-D clippy::precedence` implied by `-D warnings`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:18:5
+ |
+LL | 1 + 2 << 3;
+ | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:19:5
+ |
+LL | 4 >> 1 + 1;
+ | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:20:5
+ |
+LL | 1 + 3 >> 2;
+ | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:21:5
+ |
+LL | 1 ^ 1 - 1;
+ | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:22:5
+ |
+LL | 3 | 2 - 1;
+ | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)`
+
+error: operator precedence can trip the unwary
+ --> $DIR/precedence.rs:23:5
+ |
+LL | 3 & 5 - 2;
+ | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
+
+error: unary minus has lower precedence than method call
+ --> $DIR/precedence.rs:24:5
+ |
+LL | -1i32.abs();
+ | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())`
+
+error: unary minus has lower precedence than method call
+ --> $DIR/precedence.rs:25:5
+ |
+LL | -1f32.abs();
+ | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())`
+
+error: unary minus has lower precedence than method call
+ --> $DIR/precedence.rs:52:13
+ |
+LL | let _ = -1.0_f64.cos().cos();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())`
+
+error: unary minus has lower precedence than method call
+ --> $DIR/precedence.rs:53:13
+ |
+LL | let _ = -1.0_f64.cos().sin();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())`
+
+error: unary minus has lower precedence than method call
+ --> $DIR/precedence.rs:54:13
+ |
+LL | let _ = -1.0_f64.sin().cos();
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print.rs b/src/tools/clippy/tests/ui/print.rs
new file mode 100644
index 000000000..366ccc2b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print.rs
@@ -0,0 +1,35 @@
+#![allow(clippy::print_literal, clippy::write_literal)]
+#![warn(clippy::print_stdout, clippy::use_debug)]
+
+use std::fmt::{Debug, Display, Formatter, Result};
+
+#[allow(dead_code)]
+struct Foo;
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut Formatter) -> Result {
+ write!(f, "{:?}", 43.1415)
+ }
+}
+
+impl Debug for Foo {
+ fn fmt(&self, f: &mut Formatter) -> Result {
+ // ok, we can use `Debug` formatting in `Debug` implementations
+ write!(f, "{:?}", 42.718)
+ }
+}
+
+fn main() {
+ println!("Hello");
+ print!("Hello");
+
+ print!("Hello {}", "World");
+
+ print!("Hello {:?}", "World");
+
+ print!("Hello {:#?}", "#orld");
+
+ assert_eq!(42, 1337);
+
+ vec![1, 2];
+}
diff --git a/src/tools/clippy/tests/ui/print.stderr b/src/tools/clippy/tests/ui/print.stderr
new file mode 100644
index 000000000..1754c4183
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print.stderr
@@ -0,0 +1,54 @@
+error: use of `Debug`-based formatting
+ --> $DIR/print.rs:11:20
+ |
+LL | write!(f, "{:?}", 43.1415)
+ | ^^^^
+ |
+ = note: `-D clippy::use-debug` implied by `-D warnings`
+
+error: use of `println!`
+ --> $DIR/print.rs:23:5
+ |
+LL | println!("Hello");
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::print-stdout` implied by `-D warnings`
+
+error: use of `print!`
+ --> $DIR/print.rs:24:5
+ |
+LL | print!("Hello");
+ | ^^^^^^^^^^^^^^^
+
+error: use of `print!`
+ --> $DIR/print.rs:26:5
+ |
+LL | print!("Hello {}", "World");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of `print!`
+ --> $DIR/print.rs:28:5
+ |
+LL | print!("Hello {:?}", "World");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of `Debug`-based formatting
+ --> $DIR/print.rs:28:19
+ |
+LL | print!("Hello {:?}", "World");
+ | ^^^^
+
+error: use of `print!`
+ --> $DIR/print.rs:30:5
+ |
+LL | print!("Hello {:#?}", "#orld");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of `Debug`-based formatting
+ --> $DIR/print.rs:30:19
+ |
+LL | print!("Hello {:#?}", "#orld");
+ | ^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_in_format_impl.rs b/src/tools/clippy/tests/ui/print_in_format_impl.rs
new file mode 100644
index 000000000..64e886866
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_in_format_impl.rs
@@ -0,0 +1,58 @@
+#![allow(unused, clippy::print_literal, clippy::write_literal)]
+#![warn(clippy::print_in_format_impl)]
+use std::fmt::{Debug, Display, Error, Formatter};
+
+macro_rules! indirect {
+ () => {{ println!() }};
+}
+
+macro_rules! nested {
+ ($($tt:tt)*) => {
+ $($tt)*
+ };
+}
+
+struct Foo;
+impl Debug for Foo {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+ static WORKS_WITH_NESTED_ITEMS: bool = true;
+
+ print!("{}", 1);
+ println!("{}", 2);
+ eprint!("{}", 3);
+ eprintln!("{}", 4);
+ nested! {
+ println!("nested");
+ };
+
+ write!(f, "{}", 5);
+ writeln!(f, "{}", 6);
+ indirect!();
+
+ Ok(())
+ }
+}
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+ print!("Display");
+ write!(f, "Display");
+
+ Ok(())
+ }
+}
+
+struct UnnamedFormatter;
+impl Debug for UnnamedFormatter {
+ fn fmt(&self, _: &mut Formatter) -> Result<(), Error> {
+ println!("UnnamedFormatter");
+ Ok(())
+ }
+}
+
+fn main() {
+ print!("outside fmt");
+ println!("outside fmt");
+ eprint!("outside fmt");
+ eprintln!("outside fmt");
+}
diff --git a/src/tools/clippy/tests/ui/print_in_format_impl.stderr b/src/tools/clippy/tests/ui/print_in_format_impl.stderr
new file mode 100644
index 000000000..63b7179bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_in_format_impl.stderr
@@ -0,0 +1,46 @@
+error: use of `print!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:20:9
+ |
+LL | print!("{}", 1);
+ | ^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
+ |
+ = note: `-D clippy::print-in-format-impl` implied by `-D warnings`
+
+error: use of `println!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:21:9
+ |
+LL | println!("{}", 2);
+ | ^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
+
+error: use of `eprint!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:22:9
+ |
+LL | eprint!("{}", 3);
+ | ^^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
+
+error: use of `eprintln!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:23:9
+ |
+LL | eprintln!("{}", 4);
+ | ^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
+
+error: use of `println!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:25:13
+ |
+LL | println!("nested");
+ | ^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
+
+error: use of `print!` in `Display` impl
+ --> $DIR/print_in_format_impl.rs:38:9
+ |
+LL | print!("Display");
+ | ^^^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
+
+error: use of `println!` in `Debug` impl
+ --> $DIR/print_in_format_impl.rs:48:9
+ |
+LL | println!("UnnamedFormatter");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(..)`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
new file mode 100644
index 000000000..8665a3bb2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -0,0 +1,38 @@
+#![warn(clippy::print_literal)]
+
+fn main() {
+ // these should be fine
+ print!("Hello");
+ println!("Hello");
+ let world = "world";
+ println!("Hello {}", world);
+ println!("Hello {world}", world = world);
+ println!("3 in hex is {:X}", 3);
+ println!("2 + 1 = {:.4}", 3);
+ println!("2 + 1 = {:5.4}", 3);
+ println!("Debug test {:?}", "hello, world");
+ println!("{0:8} {1:>8}", "hello", "world");
+ println!("{1:8} {0:>8}", "hello", "world");
+ println!("{foo:8} {bar:>8}", foo = "hello", bar = "world");
+ println!("{bar:8} {foo:>8}", foo = "hello", bar = "world");
+ println!("{number:>width$}", number = 1, width = 6);
+ println!("{number:>0width$}", number = 1, width = 6);
+ println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
+ println!("10 / 4 is {}", 2.5);
+ println!("2 + 1 = {}", 3);
+
+ // these should throw warnings
+ print!("Hello {}", "world");
+ println!("Hello {} {}", world, "world");
+ println!("Hello {}", "world");
+
+ // positional args don't change the fact
+ // that we're using a literal -- this should
+ // throw a warning
+ println!("{0} {1}", "hello", "world");
+ println!("{1} {0}", "hello", "world");
+
+ // named args shouldn't change anything either
+ println!("{foo} {bar}", foo = "hello", bar = "world");
+ println!("{bar} {foo}", foo = "hello", bar = "world");
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr
new file mode 100644
index 000000000..72aae0756
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_literal.stderr
@@ -0,0 +1,135 @@
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:25:24
+ |
+LL | print!("Hello {}", "world");
+ | ^^^^^^^
+ |
+ = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+ |
+LL - print!("Hello {}", "world");
+LL + print!("Hello world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:26:36
+ |
+LL | println!("Hello {} {}", world, "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("Hello {} {}", world, "world");
+LL + println!("Hello {} world", world);
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:27:26
+ |
+LL | println!("Hello {}", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("Hello {}", "world");
+LL + println!("Hello world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:32:25
+ |
+LL | println!("{0} {1}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{0} {1}", "hello", "world");
+LL + println!("hello {1}", "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:32:34
+ |
+LL | println!("{0} {1}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{0} {1}", "hello", "world");
+LL + println!("{0} world", "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:33:25
+ |
+LL | println!("{1} {0}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{1} {0}", "hello", "world");
+LL + println!("{1} hello", "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:33:34
+ |
+LL | println!("{1} {0}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{1} {0}", "hello", "world");
+LL + println!("world {0}", "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:36:29
+ |
+LL | println!("{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{foo} {bar}", foo = "hello", bar = "world");
+LL + println!("hello {bar}", bar = "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:36:44
+ |
+LL | println!("{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{foo} {bar}", foo = "hello", bar = "world");
+LL + println!("{foo} world", foo = "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:37:29
+ |
+LL | println!("{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{bar} {foo}", foo = "hello", bar = "world");
+LL + println!("{bar} hello", bar = "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/print_literal.rs:37:44
+ |
+LL | println!("{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - println!("{bar} {foo}", foo = "hello", bar = "world");
+LL + println!("world {foo}", foo = "hello");
+ |
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_stderr.rs b/src/tools/clippy/tests/ui/print_stderr.rs
new file mode 100644
index 000000000..fa07e74a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_stderr.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::print_stderr)]
+
+fn main() {
+ eprintln!("Hello");
+ println!("This should not do anything");
+ eprint!("World");
+ print!("Nor should this");
+}
diff --git a/src/tools/clippy/tests/ui/print_stderr.stderr b/src/tools/clippy/tests/ui/print_stderr.stderr
new file mode 100644
index 000000000..5af735af6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_stderr.stderr
@@ -0,0 +1,16 @@
+error: use of `eprintln!`
+ --> $DIR/print_stderr.rs:4:5
+ |
+LL | eprintln!("Hello");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::print-stderr` implied by `-D warnings`
+
+error: use of `eprint!`
+ --> $DIR/print_stderr.rs:6:5
+ |
+LL | eprint!("World");
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_stdout_build_script.rs b/src/tools/clippy/tests/ui/print_stdout_build_script.rs
new file mode 100644
index 000000000..997ebef8a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_stdout_build_script.rs
@@ -0,0 +1,12 @@
+// compile-flags: --crate-name=build_script_build
+
+#![warn(clippy::print_stdout)]
+
+fn main() {
+ // Fix #6041
+ //
+ // The `print_stdout` lint shouldn't emit in `build.rs`
+ // as these methods are used for the build script.
+ println!("Hello");
+ print!("Hello");
+}
diff --git a/src/tools/clippy/tests/ui/print_with_newline.rs b/src/tools/clippy/tests/ui/print_with_newline.rs
new file mode 100644
index 000000000..a43a1fc4f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_with_newline.rs
@@ -0,0 +1,52 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+// // run-rustfix
+
+#![allow(clippy::print_literal)]
+#![warn(clippy::print_with_newline)]
+
+fn main() {
+ print!("Hello\n");
+ print!("Hello {}\n", "world");
+ print!("Hello {} {}\n", "world", "#2");
+ print!("{}\n", 1265);
+ print!("\n");
+
+ // these are all fine
+ print!("");
+ print!("Hello");
+ println!("Hello");
+ println!("Hello\n");
+ println!("Hello {}\n", "world");
+ print!("Issue\n{}", 1265);
+ print!("{}", 1265);
+ print!("\n{}", 1275);
+ print!("\n\n");
+ print!("like eof\n\n");
+ print!("Hello {} {}\n\n", "world", "#2");
+ println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ println!("\nbla\n\n"); // #3126
+
+ // Escaping
+ print!("\\n"); // #3514
+ print!("\\\n"); // should fail
+ print!("\\\\n");
+
+ // Raw strings
+ print!(r"\n"); // #3778
+
+ // Literal newlines should also fail
+ print!(
+ "
+"
+ );
+ print!(
+ r"
+"
+ );
+
+ // Don't warn on CRLF (#4208)
+ print!("\r\n");
+ print!("foo\r\n");
+ print!("\\r\n"); //~ ERROR
+ print!("foo\rbar\n") // ~ ERROR
+}
diff --git a/src/tools/clippy/tests/ui/print_with_newline.stderr b/src/tools/clippy/tests/ui/print_with_newline.stderr
new file mode 100644
index 000000000..edbaa1cdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_with_newline.stderr
@@ -0,0 +1,129 @@
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:8:5
+ |
+LL | print!("Hello/n");
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::print-with-newline` implied by `-D warnings`
+help: use `println!` instead
+ |
+LL - print!("Hello/n");
+LL + println!("Hello");
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:9:5
+ |
+LL | print!("Hello {}/n", "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("Hello {}/n", "world");
+LL + println!("Hello {}", "world");
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:10:5
+ |
+LL | print!("Hello {} {}/n", "world", "#2");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("Hello {} {}/n", "world", "#2");
+LL + println!("Hello {} {}", "world", "#2");
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:11:5
+ |
+LL | print!("{}/n", 1265);
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("{}/n", 1265);
+LL + println!("{}", 1265);
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:12:5
+ |
+LL | print!("/n");
+ | ^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("/n");
+LL + println!();
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:31:5
+ |
+LL | print!("//n"); // should fail
+ | ^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("//n"); // should fail
+LL + println!("/"); // should fail
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:38:5
+ |
+LL | / print!(
+LL | | "
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `println!` instead
+ |
+LL ~ println!(
+LL ~ ""
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:42:5
+ |
+LL | / print!(
+LL | | r"
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `println!` instead
+ |
+LL ~ println!(
+LL ~ r""
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:50:5
+ |
+LL | print!("/r/n"); //~ ERROR
+ | ^^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("/r/n"); //~ ERROR
+LL + println!("/r"); //~ ERROR
+ |
+
+error: using `print!()` with a format string that ends in a single newline
+ --> $DIR/print_with_newline.rs:51:5
+ |
+LL | print!("foo/rbar/n") // ~ ERROR
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `println!` instead
+ |
+LL - print!("foo/rbar/n") // ~ ERROR
+LL + println!("foo/rbar") // ~ ERROR
+ |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/println_empty_string.fixed b/src/tools/clippy/tests/ui/println_empty_string.fixed
new file mode 100644
index 000000000..976068092
--- /dev/null
+++ b/src/tools/clippy/tests/ui/println_empty_string.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+#![allow(clippy::match_single_binding)]
+
+fn main() {
+ println!();
+ println!();
+
+ match "a" {
+ _ => println!(),
+ }
+
+ eprintln!();
+ eprintln!();
+
+ match "a" {
+ _ => eprintln!(),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/println_empty_string.rs b/src/tools/clippy/tests/ui/println_empty_string.rs
new file mode 100644
index 000000000..80fdb3e6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/println_empty_string.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+#![allow(clippy::match_single_binding)]
+
+fn main() {
+ println!();
+ println!("");
+
+ match "a" {
+ _ => println!(""),
+ }
+
+ eprintln!();
+ eprintln!("");
+
+ match "a" {
+ _ => eprintln!(""),
+ }
+}
diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr
new file mode 100644
index 000000000..17fe4ea74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/println_empty_string.stderr
@@ -0,0 +1,28 @@
+error: using `println!("")`
+ --> $DIR/println_empty_string.rs:6:5
+ |
+LL | println!("");
+ | ^^^^^^^^^^^^ help: replace it with: `println!()`
+ |
+ = note: `-D clippy::println-empty-string` implied by `-D warnings`
+
+error: using `println!("")`
+ --> $DIR/println_empty_string.rs:9:14
+ |
+LL | _ => println!(""),
+ | ^^^^^^^^^^^^ help: replace it with: `println!()`
+
+error: using `eprintln!("")`
+ --> $DIR/println_empty_string.rs:13:5
+ |
+LL | eprintln!("");
+ | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: using `eprintln!("")`
+ --> $DIR/println_empty_string.rs:16:14
+ |
+LL | _ => eprintln!(""),
+ | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/proc_macro.rs b/src/tools/clippy/tests/ui/proc_macro.rs
new file mode 100644
index 000000000..59914b8b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/proc_macro.rs
@@ -0,0 +1,26 @@
+//! Check that we correctly lint procedural macros.
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[allow(dead_code)]
+fn f() {
+ let _x = 3.14;
+}
+
+#[proc_macro]
+pub fn mybangmacro(t: TokenStream) -> TokenStream {
+ t
+}
+
+#[proc_macro_derive(MyDerivedTrait)]
+pub fn myderive(t: TokenStream) -> TokenStream {
+ t
+}
+
+#[proc_macro_attribute]
+pub fn myattribute(t: TokenStream, a: TokenStream) -> TokenStream {
+ t
+}
diff --git a/src/tools/clippy/tests/ui/proc_macro.stderr b/src/tools/clippy/tests/ui/proc_macro.stderr
new file mode 100644
index 000000000..48fd58c9a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/proc_macro.stderr
@@ -0,0 +1,11 @@
+error: approximate value of `f{32, 64}::consts::PI` found
+ --> $DIR/proc_macro.rs:10:14
+ |
+LL | let _x = 3.14;
+ | ^^^^
+ |
+ = note: `#[deny(clippy::approx_constant)]` on by default
+ = help: consider using the constant directly
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
new file mode 100644
index 000000000..fd15001e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -0,0 +1,209 @@
+#![feature(lint_reasons)]
+#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#![warn(clippy::ptr_arg)]
+
+use std::borrow::Cow;
+use std::path::PathBuf;
+
+fn do_vec(x: &Vec<i64>) {
+ //Nothing here
+}
+
+fn do_vec_mut(x: &mut Vec<i64>) {
+ //Nothing here
+}
+
+fn do_str(x: &String) {
+ //Nothing here either
+}
+
+fn do_str_mut(x: &mut String) {
+ //Nothing here either
+}
+
+fn do_path(x: &PathBuf) {
+ //Nothing here either
+}
+
+fn do_path_mut(x: &mut PathBuf) {
+ //Nothing here either
+}
+
+fn main() {}
+
+trait Foo {
+ type Item;
+ fn do_vec(x: &Vec<i64>);
+ fn do_item(x: &Self::Item);
+}
+
+struct Bar;
+
+// no error, in trait impl (#425)
+impl Foo for Bar {
+ type Item = Vec<u8>;
+ fn do_vec(x: &Vec<i64>) {}
+ fn do_item(x: &Vec<u8>) {}
+}
+
+fn cloned(x: &Vec<u8>) -> Vec<u8> {
+ let e = x.clone();
+ let f = e.clone(); // OK
+ let g = x;
+ let h = g.clone();
+ let i = (e).clone();
+ x.clone()
+}
+
+fn str_cloned(x: &String) -> String {
+ let a = x.clone();
+ let b = x.clone();
+ let c = b.clone();
+ let d = a.clone().clone().clone();
+ x.clone()
+}
+
+fn path_cloned(x: &PathBuf) -> PathBuf {
+ let a = x.clone();
+ let b = x.clone();
+ let c = b.clone();
+ let d = a.clone().clone().clone();
+ x.clone()
+}
+
+fn false_positive_capacity(x: &Vec<u8>, y: &String) {
+ let a = x.capacity();
+ let b = y.clone();
+ let c = y.as_str();
+}
+
+fn false_positive_capacity_too(x: &String) -> String {
+ if x.capacity() > 1024 {
+ panic!("Too large!");
+ }
+ x.clone()
+}
+
+#[allow(dead_code)]
+fn test_cow_with_ref(c: &Cow<[i32]>) {}
+
+fn test_cow(c: Cow<[i32]>) {
+ let _c = c;
+}
+
+trait Foo2 {
+ fn do_string(&self);
+}
+
+// no error for &self references where self is of type String (#2293)
+impl Foo2 for String {
+ fn do_string(&self) {}
+}
+
+// Check that the allow attribute on parameters is honored
+mod issue_5644 {
+ use std::borrow::Cow;
+ use std::path::PathBuf;
+
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
+ ) {
+ }
+
+ fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+
+ struct S;
+ impl S {
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
+ ) {
+ }
+ }
+
+ trait T {
+ fn allowed(
+ #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+ #[allow(clippy::ptr_arg)] _s: &String,
+ #[allow(clippy::ptr_arg)] _p: &PathBuf,
+ #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+ #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
+ ) {
+ }
+ }
+}
+
+mod issue6509 {
+ use std::path::PathBuf;
+
+ fn foo_vec(vec: &Vec<u8>) {
+ let _ = vec.clone().pop();
+ let _ = vec.clone().clone();
+ }
+
+ fn foo_path(path: &PathBuf) {
+ let _ = path.clone().pop();
+ let _ = path.clone().clone();
+ }
+
+ fn foo_str(str: &PathBuf) {
+ let _ = str.clone().pop();
+ let _ = str.clone().clone();
+ }
+}
+
+fn mut_vec_slice_methods(v: &mut Vec<u32>) {
+ v.copy_within(1..5, 10);
+}
+
+fn mut_vec_vec_methods(v: &mut Vec<u32>) {
+ v.clear();
+}
+
+fn vec_contains(v: &Vec<u32>) -> bool {
+ [vec![], vec![0]].as_slice().contains(v)
+}
+
+fn fn_requires_vec(v: &Vec<u32>) -> bool {
+ vec_contains(v)
+}
+
+fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) {
+ f(v);
+}
+
+fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) {
+ f(v);
+}
+
+// No error for types behind an alias (#7699)
+type A = Vec<u8>;
+fn aliased(a: &A) {}
+
+// Issue #8366
+pub trait Trait {
+ fn f(v: &mut Vec<i32>);
+ fn f2(v: &mut Vec<i32>) {}
+}
+
+// Issue #8463
+fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) {
+ a.push(0);
+ a.push(0);
+ a.push(0);
+ b.push(1);
+}
+
+// Issue #8495
+fn cow_conditional_to_mut(a: &mut Cow<str>) {
+ if a.is_empty() {
+ a.to_mut().push_str("foo");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
new file mode 100644
index 000000000..d64b5f454
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -0,0 +1,166 @@
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:8:14
+ |
+LL | fn do_vec(x: &Vec<i64>) {
+ | ^^^^^^^^^ help: change this to: `&[i64]`
+ |
+ = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:12:18
+ |
+LL | fn do_vec_mut(x: &mut Vec<i64>) {
+ | ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:16:14
+ |
+LL | fn do_str(x: &String) {
+ | ^^^^^^^ help: change this to: `&str`
+
+error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:20:18
+ |
+LL | fn do_str_mut(x: &mut String) {
+ | ^^^^^^^^^^^ help: change this to: `&mut str`
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:24:15
+ |
+LL | fn do_path(x: &PathBuf) {
+ | ^^^^^^^^ help: change this to: `&Path`
+
+error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:28:19
+ |
+LL | fn do_path_mut(x: &mut PathBuf) {
+ | ^^^^^^^^^^^^ help: change this to: `&mut Path`
+
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:36:18
+ |
+LL | fn do_vec(x: &Vec<i64>);
+ | ^^^^^^^^^ help: change this to: `&[i64]`
+
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:49:14
+ |
+LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn cloned(x: &[u8]) -> Vec<u8> {
+LL ~ let e = x.to_owned();
+LL | let f = e.clone(); // OK
+LL | let g = x;
+LL ~ let h = g.to_owned();
+LL | let i = (e).clone();
+LL ~ x.to_owned()
+ |
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:58:18
+ |
+LL | fn str_cloned(x: &String) -> String {
+ | ^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn str_cloned(x: &str) -> String {
+LL ~ let a = x.to_owned();
+LL ~ let b = x.to_owned();
+LL | let c = b.clone();
+LL | let d = a.clone().clone().clone();
+LL ~ x.to_owned()
+ |
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:66:19
+ |
+LL | fn path_cloned(x: &PathBuf) -> PathBuf {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn path_cloned(x: &Path) -> PathBuf {
+LL ~ let a = x.to_path_buf();
+LL ~ let b = x.to_path_buf();
+LL | let c = b.clone();
+LL | let d = a.clone().clone().clone();
+LL ~ x.to_path_buf()
+ |
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:74:44
+ |
+LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
+ | ^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn false_positive_capacity(x: &Vec<u8>, y: &str) {
+LL | let a = x.capacity();
+LL ~ let b = y.to_owned();
+LL ~ let c = y;
+ |
+
+error: using a reference to `Cow` is not recommended
+ --> $DIR/ptr_arg.rs:88:25
+ |
+LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
+ | ^^^^^^^^^^^ help: change this to: `&[i32]`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:117:66
+ |
+LL | fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+ | ^^^^^^^ help: change this to: `&str`
+
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:146:21
+ |
+LL | fn foo_vec(vec: &Vec<u8>) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn foo_vec(vec: &[u8]) {
+LL ~ let _ = vec.to_owned().pop();
+LL ~ let _ = vec.to_owned().clone();
+ |
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:151:23
+ |
+LL | fn foo_path(path: &PathBuf) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn foo_path(path: &Path) {
+LL ~ let _ = path.to_path_buf().pop();
+LL ~ let _ = path.to_path_buf().clone();
+ |
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:156:21
+ |
+LL | fn foo_str(str: &PathBuf) {
+ | ^^^^^^^^
+ |
+help: change this to
+ |
+LL ~ fn foo_str(str: &Path) {
+LL ~ let _ = str.to_path_buf().pop();
+LL ~ let _ = str.to_path_buf().clone();
+ |
+
+error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:162:29
+ |
+LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
+ | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
new file mode 100644
index 000000000..bea6be66a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -0,0 +1,65 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::ptr_as_ptr)]
+#![feature(custom_inner_attributes)]
+
+extern crate macro_rules;
+
+macro_rules! cast_it {
+ ($ptr: ident) => {
+ $ptr.cast::<i32>()
+ };
+}
+
+fn main() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr.cast::<i32>();
+ let _ = mut_ptr.cast::<i32>();
+
+ // Make sure the lint can handle the difference in their operator precedences.
+ unsafe {
+ let ptr_ptr: *const *const u32 = &ptr;
+ let _ = (*ptr_ptr).cast::<i32>();
+ }
+
+ // Changes in mutability. Do not lint this.
+ let _ = ptr as *mut i32;
+ let _ = mut_ptr as *const i32;
+
+ // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
+ let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
+ let _ = ptr_of_array as *const [u32];
+ let _ = ptr_of_array as *const dyn std::fmt::Debug;
+
+ // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
+ let _: *const i32 = ptr.cast();
+ let _: *mut i32 = mut_ptr.cast();
+
+ // Make sure the lint is triggered inside a macro
+ let _ = cast_it!(ptr);
+
+ // Do not lint inside macros from external crates
+ let _ = macro_rules::ptr_as_ptr_cast!(ptr);
+}
+
+fn _msrv_1_37() {
+ #![clippy::msrv = "1.37"]
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ // `pointer::cast` was stabilized in 1.38. Do not lint this
+ let _ = ptr as *const i32;
+ let _ = mut_ptr as *mut i32;
+}
+
+fn _msrv_1_38() {
+ #![clippy::msrv = "1.38"]
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr.cast::<i32>();
+ let _ = mut_ptr.cast::<i32>();
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
new file mode 100644
index 000000000..ca2616b00
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -0,0 +1,65 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::ptr_as_ptr)]
+#![feature(custom_inner_attributes)]
+
+extern crate macro_rules;
+
+macro_rules! cast_it {
+ ($ptr: ident) => {
+ $ptr as *const i32
+ };
+}
+
+fn main() {
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr as *const i32;
+ let _ = mut_ptr as *mut i32;
+
+ // Make sure the lint can handle the difference in their operator precedences.
+ unsafe {
+ let ptr_ptr: *const *const u32 = &ptr;
+ let _ = *ptr_ptr as *const i32;
+ }
+
+ // Changes in mutability. Do not lint this.
+ let _ = ptr as *mut i32;
+ let _ = mut_ptr as *const i32;
+
+ // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
+ let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
+ let _ = ptr_of_array as *const [u32];
+ let _ = ptr_of_array as *const dyn std::fmt::Debug;
+
+ // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
+ let _: *const i32 = ptr as *const _;
+ let _: *mut i32 = mut_ptr as _;
+
+ // Make sure the lint is triggered inside a macro
+ let _ = cast_it!(ptr);
+
+ // Do not lint inside macros from external crates
+ let _ = macro_rules::ptr_as_ptr_cast!(ptr);
+}
+
+fn _msrv_1_37() {
+ #![clippy::msrv = "1.37"]
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ // `pointer::cast` was stabilized in 1.38. Do not lint this
+ let _ = ptr as *const i32;
+ let _ = mut_ptr as *mut i32;
+}
+
+fn _msrv_1_38() {
+ #![clippy::msrv = "1.38"]
+ let ptr: *const u32 = &42_u32;
+ let mut_ptr: *mut u32 = &mut 42_u32;
+
+ let _ = ptr as *const i32;
+ let _ = mut_ptr as *mut i32;
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
new file mode 100644
index 000000000..c58c55cfd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -0,0 +1,57 @@
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:19:13
+ |
+LL | let _ = ptr as *const i32;
+ | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
+ |
+ = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:20:13
+ |
+LL | let _ = mut_ptr as *mut i32;
+ | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:25:17
+ |
+LL | let _ = *ptr_ptr as *const i32;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:38:25
+ |
+LL | let _: *const i32 = ptr as *const _;
+ | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:39:23
+ |
+LL | let _: *mut i32 = mut_ptr as _;
+ | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:11:9
+ |
+LL | $ptr as *const i32
+ | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
+...
+LL | let _ = cast_it!(ptr);
+ | ------------- in this macro invocation
+ |
+ = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:63:13
+ |
+LL | let _ = ptr as *const i32;
+ | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
+
+error: `as` casting between raw pointers without changing its mutability
+ --> $DIR/ptr_as_ptr.rs:64:13
+ |
+LL | let _ = mut_ptr as *mut i32;
+ | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_eq.fixed b/src/tools/clippy/tests/ui/ptr_eq.fixed
new file mode 100644
index 000000000..209081e6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_eq.fixed
@@ -0,0 +1,38 @@
+// run-rustfix
+#![warn(clippy::ptr_eq)]
+
+macro_rules! mac {
+ ($a:expr, $b:expr) => {
+ $a as *const _ as usize == $b as *const _ as usize
+ };
+}
+
+macro_rules! another_mac {
+ ($a:expr, $b:expr) => {
+ $a as *const _ == $b as *const _
+ };
+}
+
+fn main() {
+ let a = &[1, 2, 3];
+ let b = &[1, 2, 3];
+
+ let _ = std::ptr::eq(a, b);
+ let _ = std::ptr::eq(a, b);
+ let _ = a.as_ptr() == b as *const _;
+ let _ = a.as_ptr() == b.as_ptr();
+
+ // Do not lint
+
+ let _ = mac!(a, b);
+ let _ = another_mac!(a, b);
+
+ let a = &mut [1, 2, 3];
+ let b = &mut [1, 2, 3];
+
+ let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _;
+ let _ = a.as_mut_ptr() == b.as_mut_ptr();
+
+ let _ = a == b;
+ let _ = core::ptr::eq(a, b);
+}
diff --git a/src/tools/clippy/tests/ui/ptr_eq.rs b/src/tools/clippy/tests/ui/ptr_eq.rs
new file mode 100644
index 000000000..691628708
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_eq.rs
@@ -0,0 +1,38 @@
+// run-rustfix
+#![warn(clippy::ptr_eq)]
+
+macro_rules! mac {
+ ($a:expr, $b:expr) => {
+ $a as *const _ as usize == $b as *const _ as usize
+ };
+}
+
+macro_rules! another_mac {
+ ($a:expr, $b:expr) => {
+ $a as *const _ == $b as *const _
+ };
+}
+
+fn main() {
+ let a = &[1, 2, 3];
+ let b = &[1, 2, 3];
+
+ let _ = a as *const _ as usize == b as *const _ as usize;
+ let _ = a as *const _ == b as *const _;
+ let _ = a.as_ptr() == b as *const _;
+ let _ = a.as_ptr() == b.as_ptr();
+
+ // Do not lint
+
+ let _ = mac!(a, b);
+ let _ = another_mac!(a, b);
+
+ let a = &mut [1, 2, 3];
+ let b = &mut [1, 2, 3];
+
+ let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _;
+ let _ = a.as_mut_ptr() == b.as_mut_ptr();
+
+ let _ = a == b;
+ let _ = core::ptr::eq(a, b);
+}
diff --git a/src/tools/clippy/tests/ui/ptr_eq.stderr b/src/tools/clippy/tests/ui/ptr_eq.stderr
new file mode 100644
index 000000000..45d8c6038
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_eq.stderr
@@ -0,0 +1,16 @@
+error: use `std::ptr::eq` when comparing raw pointers
+ --> $DIR/ptr_eq.rs:20:13
+ |
+LL | let _ = a as *const _ as usize == b as *const _ as usize;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)`
+ |
+ = note: `-D clippy::ptr-eq` implied by `-D warnings`
+
+error: use `std::ptr::eq` when comparing raw pointers
+ --> $DIR/ptr_eq.rs:21:13
+ |
+LL | let _ = a as *const _ == b as *const _;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
new file mode 100644
index 000000000..718e391e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
@@ -0,0 +1,20 @@
+// run-rustfix
+
+fn main() {
+ let vec = vec![b'a', b'b', b'c'];
+ let ptr = vec.as_ptr();
+
+ let offset_u8 = 1_u8;
+ let offset_usize = 1_usize;
+ let offset_isize = 1_isize;
+
+ unsafe {
+ let _ = ptr.add(offset_usize);
+ let _ = ptr.offset(offset_isize as isize);
+ let _ = ptr.offset(offset_u8 as isize);
+
+ let _ = ptr.wrapping_add(offset_usize);
+ let _ = ptr.wrapping_offset(offset_isize as isize);
+ let _ = ptr.wrapping_offset(offset_u8 as isize);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
new file mode 100644
index 000000000..f613742c7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+
+fn main() {
+ let vec = vec![b'a', b'b', b'c'];
+ let ptr = vec.as_ptr();
+
+ let offset_u8 = 1_u8;
+ let offset_usize = 1_usize;
+ let offset_isize = 1_isize;
+
+ unsafe {
+ let _ = ptr.offset(offset_usize as isize);
+ let _ = ptr.offset(offset_isize as isize);
+ let _ = ptr.offset(offset_u8 as isize);
+
+ let _ = ptr.wrapping_offset(offset_usize as isize);
+ let _ = ptr.wrapping_offset(offset_isize as isize);
+ let _ = ptr.wrapping_offset(offset_u8 as isize);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
new file mode 100644
index 000000000..fd45224ca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
@@ -0,0 +1,16 @@
+error: use of `offset` with a `usize` casted to an `isize`
+ --> $DIR/ptr_offset_with_cast.rs:12:17
+ |
+LL | let _ = ptr.offset(offset_usize as isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
+ |
+ = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings`
+
+error: use of `wrapping_offset` with a `usize` casted to an `isize`
+ --> $DIR/ptr_offset_with_cast.rs:16:17
+ |
+LL | let _ = ptr.wrapping_offset(offset_usize as isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pub_use.rs b/src/tools/clippy/tests/ui/pub_use.rs
new file mode 100644
index 000000000..65542bede
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_use.rs
@@ -0,0 +1,14 @@
+#![warn(clippy::pub_use)]
+#![allow(unused_imports)]
+#![no_main]
+
+pub mod outer {
+ mod inner {
+ pub struct Test {}
+ }
+ // should be linted
+ pub use inner::Test;
+}
+
+// should not be linted
+use std::fmt;
diff --git a/src/tools/clippy/tests/ui/pub_use.stderr b/src/tools/clippy/tests/ui/pub_use.stderr
new file mode 100644
index 000000000..9ab710df8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pub_use.stderr
@@ -0,0 +1,11 @@
+error: using `pub use`
+ --> $DIR/pub_use.rs:10:5
+ |
+LL | pub use inner::Test;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pub-use` implied by `-D warnings`
+ = help: move the exported item to a public module instead
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed
new file mode 100644
index 000000000..c4c9c8214
--- /dev/null
+++ b/src/tools/clippy/tests/ui/question_mark.fixed
@@ -0,0 +1,210 @@
+// run-rustfix
+#![allow(unreachable_code)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn some_func(a: Option<u32>) -> Option<u32> {
+ a?;
+
+ a
+}
+
+fn some_other_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ } else {
+ return Some(0);
+ }
+ unreachable!()
+}
+
+pub enum SeemsOption<T> {
+ Some(T),
+ None,
+}
+
+impl<T> SeemsOption<T> {
+ pub fn is_none(&self) -> bool {
+ match *self {
+ SeemsOption::None => true,
+ SeemsOption::Some(_) => false,
+ }
+ }
+}
+
+fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
+ if a.is_none() {
+ return SeemsOption::None;
+ }
+
+ a
+}
+
+pub struct CopyStruct {
+ pub opt: Option<u32>,
+}
+
+impl CopyStruct {
+ #[rustfmt::skip]
+ pub fn func(&self) -> Option<u32> {
+ (self.opt)?;
+
+ self.opt?;
+
+ let _ = Some(self.opt?);
+
+ let _ = self.opt?;
+
+ self.opt
+ }
+}
+
+#[derive(Clone)]
+pub struct MoveStruct {
+ pub opt: Option<Vec<u32>>,
+}
+
+impl MoveStruct {
+ pub fn ref_func(&self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+
+ self.opt.clone()
+ }
+
+ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+
+ self.opt
+ }
+
+ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
+ self.opt.as_ref()?;
+ Some(Vec::new())
+ }
+
+ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
+ let v: &Vec<_> = self.opt.as_ref()?;
+
+ Some(v.clone())
+ }
+
+ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
+ let v = self.opt?;
+
+ Some(v)
+ }
+}
+
+fn func() -> Option<i32> {
+ fn f() -> Option<String> {
+ Some(String::new())
+ }
+
+ f()?;
+
+ Some(0)
+}
+
+fn func_returning_result() -> Result<i32, i32> {
+ Ok(1)
+}
+
+fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
+ let _ = x?;
+
+ x?;
+
+ // No warning
+ let y = if let Ok(x) = x {
+ x
+ } else {
+ return Err(0);
+ };
+
+ // issue #7859
+ // no warning
+ let _ = if let Ok(x) = func_returning_result() {
+ x
+ } else {
+ return Err(0);
+ };
+
+ // no warning
+ if func_returning_result().is_err() {
+ return func_returning_result();
+ }
+
+ Ok(y)
+}
+
+// see issue #8019
+pub enum NotOption {
+ None,
+ First,
+ AfterFirst,
+}
+
+fn obj(_: i32) -> Result<(), NotOption> {
+ Err(NotOption::First)
+}
+
+fn f() -> NotOption {
+ if obj(2).is_err() {
+ return NotOption::None;
+ }
+ NotOption::First
+}
+
+fn do_something() {}
+
+fn err_immediate_return() -> Result<i32, i32> {
+ func_returning_result()?;
+ Ok(1)
+}
+
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+ func_returning_result()?;
+ do_something();
+ Ok(1)
+}
+
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ do_something();
+ return Err(err);
+ }
+ Ok(1)
+}
+
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+ if let Err(err) = func_returning_result() {
+ return Some(err);
+ }
+ None
+}
+
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+ if true {
+ Ok(1)
+ } else if let Err(e) = func_returning_result() {
+ Err(e)
+ } else {
+ Err(-1)
+ }
+}
+
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+ if let Some(a) = Some(false) {
+ Some(!a)
+ } else {
+ None
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs
new file mode 100644
index 000000000..cdbc7b160
--- /dev/null
+++ b/src/tools/clippy/tests/ui/question_mark.rs
@@ -0,0 +1,246 @@
+// run-rustfix
+#![allow(unreachable_code)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn some_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ }
+
+ a
+}
+
+fn some_other_func(a: Option<u32>) -> Option<u32> {
+ if a.is_none() {
+ return None;
+ } else {
+ return Some(0);
+ }
+ unreachable!()
+}
+
+pub enum SeemsOption<T> {
+ Some(T),
+ None,
+}
+
+impl<T> SeemsOption<T> {
+ pub fn is_none(&self) -> bool {
+ match *self {
+ SeemsOption::None => true,
+ SeemsOption::Some(_) => false,
+ }
+ }
+}
+
+fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
+ if a.is_none() {
+ return SeemsOption::None;
+ }
+
+ a
+}
+
+pub struct CopyStruct {
+ pub opt: Option<u32>,
+}
+
+impl CopyStruct {
+ #[rustfmt::skip]
+ pub fn func(&self) -> Option<u32> {
+ if (self.opt).is_none() {
+ return None;
+ }
+
+ if self.opt.is_none() {
+ return None
+ }
+
+ let _ = if self.opt.is_none() {
+ return None;
+ } else {
+ self.opt
+ };
+
+ let _ = if let Some(x) = self.opt {
+ x
+ } else {
+ return None;
+ };
+
+ self.opt
+ }
+}
+
+#[derive(Clone)]
+pub struct MoveStruct {
+ pub opt: Option<Vec<u32>>,
+}
+
+impl MoveStruct {
+ pub fn ref_func(&self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+
+ self.opt.clone()
+ }
+
+ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+
+ self.opt
+ }
+
+ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
+ if self.opt.is_none() {
+ return None;
+ }
+ Some(Vec::new())
+ }
+
+ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
+ let v: &Vec<_> = if let Some(ref v) = self.opt {
+ v
+ } else {
+ return None;
+ };
+
+ Some(v.clone())
+ }
+
+ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
+ let v = if let Some(v) = self.opt {
+ v
+ } else {
+ return None;
+ };
+
+ Some(v)
+ }
+}
+
+fn func() -> Option<i32> {
+ fn f() -> Option<String> {
+ Some(String::new())
+ }
+
+ if f().is_none() {
+ return None;
+ }
+
+ Some(0)
+}
+
+fn func_returning_result() -> Result<i32, i32> {
+ Ok(1)
+}
+
+fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
+ let _ = if let Ok(x) = x { x } else { return x };
+
+ if x.is_err() {
+ return x;
+ }
+
+ // No warning
+ let y = if let Ok(x) = x {
+ x
+ } else {
+ return Err(0);
+ };
+
+ // issue #7859
+ // no warning
+ let _ = if let Ok(x) = func_returning_result() {
+ x
+ } else {
+ return Err(0);
+ };
+
+ // no warning
+ if func_returning_result().is_err() {
+ return func_returning_result();
+ }
+
+ Ok(y)
+}
+
+// see issue #8019
+pub enum NotOption {
+ None,
+ First,
+ AfterFirst,
+}
+
+fn obj(_: i32) -> Result<(), NotOption> {
+ Err(NotOption::First)
+}
+
+fn f() -> NotOption {
+ if obj(2).is_err() {
+ return NotOption::None;
+ }
+ NotOption::First
+}
+
+fn do_something() {}
+
+fn err_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ return Err(err);
+ }
+ Ok(1)
+}
+
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ return Err(err);
+ }
+ do_something();
+ Ok(1)
+}
+
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ do_something();
+ return Err(err);
+ }
+ Ok(1)
+}
+
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+ if let Err(err) = func_returning_result() {
+ return Some(err);
+ }
+ None
+}
+
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+ if true {
+ Ok(1)
+ } else if let Err(e) = func_returning_result() {
+ Err(e)
+ } else {
+ Err(-1)
+ }
+}
+
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+ if let Some(a) = Some(false) {
+ Some(!a)
+ } else {
+ None
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr
new file mode 100644
index 000000000..1b6cd524b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/question_mark.stderr
@@ -0,0 +1,134 @@
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:7:5
+ |
+LL | / if a.is_none() {
+LL | | return None;
+LL | | }
+ | |_____^ help: replace it with: `a?;`
+ |
+ = note: `-D clippy::question-mark` implied by `-D warnings`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:52:9
+ |
+LL | / if (self.opt).is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `(self.opt)?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:56:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None
+LL | | }
+ | |_________^ help: replace it with: `self.opt?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:60:17
+ |
+LL | let _ = if self.opt.is_none() {
+ | _________________^
+LL | | return None;
+LL | | } else {
+LL | | self.opt
+LL | | };
+ | |_________^ help: replace it with: `Some(self.opt?)`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:66:17
+ |
+LL | let _ = if let Some(x) = self.opt {
+ | _________________^
+LL | | x
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:83:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:91:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:99:9
+ |
+LL | / if self.opt.is_none() {
+LL | | return None;
+LL | | }
+ | |_________^ help: replace it with: `self.opt.as_ref()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:106:26
+ |
+LL | let v: &Vec<_> = if let Some(ref v) = self.opt {
+ | __________________________^
+LL | | v
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt.as_ref()?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:116:17
+ |
+LL | let v = if let Some(v) = self.opt {
+ | _________________^
+LL | | v
+LL | | } else {
+LL | | return None;
+LL | | };
+ | |_________^ help: replace it with: `self.opt?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:131:5
+ |
+LL | / if f().is_none() {
+LL | | return None;
+LL | | }
+ | |_____^ help: replace it with: `f()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:143:13
+ |
+LL | let _ = if let Ok(x) = x { x } else { return x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:145:5
+ |
+LL | / if x.is_err() {
+LL | | return x;
+LL | | }
+ | |_____^ help: replace it with: `x?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:193:5
+ |
+LL | / if let Err(err) = func_returning_result() {
+LL | | return Err(err);
+LL | | }
+ | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:200:5
+ |
+LL | / if let Err(err) = func_returning_result() {
+LL | | return Err(err);
+LL | | }
+ | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/range.rs b/src/tools/clippy/tests/ui/range.rs
new file mode 100644
index 000000000..628282509
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range.rs
@@ -0,0 +1,16 @@
+#[warn(clippy::range_zip_with_len)]
+fn main() {
+ let v1 = vec![1, 2, 3];
+ let v2 = vec![4, 5];
+ let _x = v1.iter().zip(0..v1.len());
+ let _y = v1.iter().zip(0..v2.len()); // No error
+}
+
+#[allow(unused)]
+fn no_panic_with_fake_range_types() {
+ struct Range {
+ foo: i32,
+ }
+
+ let _ = Range { foo: 0 };
+}
diff --git a/src/tools/clippy/tests/ui/range.stderr b/src/tools/clippy/tests/ui/range.stderr
new file mode 100644
index 000000000..dcb506137
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range.stderr
@@ -0,0 +1,10 @@
+error: it is more idiomatic to use `v1.iter().enumerate()`
+ --> $DIR/range.rs:5:14
+ |
+LL | let _x = v1.iter().zip(0..v1.len());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::range-zip-with-len` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed
new file mode 100644
index 000000000..85d021b2f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.fixed
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#[warn(clippy::manual_range_contains)]
+#[allow(unused)]
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+fn main() {
+ let x = 9_i32;
+
+ // order shouldn't matter
+ (8..12).contains(&x);
+ (21..42).contains(&x);
+ (1..100).contains(&x);
+
+ // also with inclusive ranges
+ (9..=99).contains(&x);
+ (1..=33).contains(&x);
+ (1..=999).contains(&x);
+
+ // and the outside
+ !(8..12).contains(&x);
+ !(21..42).contains(&x);
+ !(1..100).contains(&x);
+
+ // also with the outside of inclusive ranges
+ !(9..=99).contains(&x);
+ !(1..=33).contains(&x);
+ !(1..=999).contains(&x);
+
+ // not a range.contains
+ x > 8 && x < 12; // lower bound not inclusive
+ x < 8 && x <= 12; // same direction
+ x >= 12 && 12 >= x; // same bounds
+ x < 8 && x > 12; // wrong direction
+
+ x <= 8 || x >= 12;
+ x >= 8 || x >= 12;
+ x < 12 || 12 < x;
+ x >= 8 || x <= 12;
+
+ // Fix #6315
+ let y = 3.;
+ (0. ..1.).contains(&y);
+ !(0. ..=1.).contains(&y);
+
+ // handle negatives #8721
+ (-10..=10).contains(&x);
+ x >= 10 && x <= -10;
+ (-3. ..=3.).contains(&y);
+ y >= 3. && y <= -3.;
+
+ // Fix #8745
+ let z = 42;
+ (0..=10).contains(&x) && (0..=10).contains(&z);
+ !(0..10).contains(&x) || !(0..10).contains(&z);
+ // Make sure operators in parens don't give a breaking suggestion
+ ((x % 2 == 0) || (x < 0)) || (x >= 10);
+}
+
+// Fix #6373
+pub const fn in_range(a: i32) -> bool {
+ 3 <= a && a <= 20
+}
diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs
new file mode 100644
index 000000000..9a7a75dc1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.rs
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#[warn(clippy::manual_range_contains)]
+#[allow(unused)]
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+fn main() {
+ let x = 9_i32;
+
+ // order shouldn't matter
+ x >= 8 && x < 12;
+ x < 42 && x >= 21;
+ 100 > x && 1 <= x;
+
+ // also with inclusive ranges
+ x >= 9 && x <= 99;
+ x <= 33 && x >= 1;
+ 999 >= x && 1 <= x;
+
+ // and the outside
+ x < 8 || x >= 12;
+ x >= 42 || x < 21;
+ 100 <= x || 1 > x;
+
+ // also with the outside of inclusive ranges
+ x < 9 || x > 99;
+ x > 33 || x < 1;
+ 999 < x || 1 > x;
+
+ // not a range.contains
+ x > 8 && x < 12; // lower bound not inclusive
+ x < 8 && x <= 12; // same direction
+ x >= 12 && 12 >= x; // same bounds
+ x < 8 && x > 12; // wrong direction
+
+ x <= 8 || x >= 12;
+ x >= 8 || x >= 12;
+ x < 12 || 12 < x;
+ x >= 8 || x <= 12;
+
+ // Fix #6315
+ let y = 3.;
+ y >= 0. && y < 1.;
+ y < 0. || y > 1.;
+
+ // handle negatives #8721
+ x >= -10 && x <= 10;
+ x >= 10 && x <= -10;
+ y >= -3. && y <= 3.;
+ y >= 3. && y <= -3.;
+
+ // Fix #8745
+ let z = 42;
+ (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
+ (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
+ // Make sure operators in parens don't give a breaking suggestion
+ ((x % 2 == 0) || (x < 0)) || (x >= 10);
+}
+
+// Fix #6373
+pub const fn in_range(a: i32) -> bool {
+ 3 <= a && a <= 20
+}
diff --git a/src/tools/clippy/tests/ui/range_contains.stderr b/src/tools/clippy/tests/ui/range_contains.stderr
new file mode 100644
index 000000000..936859db5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.stderr
@@ -0,0 +1,124 @@
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:12:5
+ |
+LL | x >= 8 && x < 12;
+ | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
+ |
+ = note: `-D clippy::manual-range-contains` implied by `-D warnings`
+
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:13:5
+ |
+LL | x < 42 && x >= 21;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
+
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:14:5
+ |
+LL | 100 > x && 1 <= x;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:17:5
+ |
+LL | x >= 9 && x <= 99;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:18:5
+ |
+LL | x <= 33 && x >= 1;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:19:5
+ |
+LL | 999 >= x && 1 <= x;
+ | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:22:5
+ |
+LL | x < 8 || x >= 12;
+ | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:23:5
+ |
+LL | x >= 42 || x < 21;
+ | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:24:5
+ |
+LL | 100 <= x || 1 > x;
+ | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:27:5
+ |
+LL | x < 9 || x > 99;
+ | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:28:5
+ |
+LL | x > 33 || x < 1;
+ | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:29:5
+ |
+LL | 999 < x || 1 > x;
+ | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
+
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:44:5
+ |
+LL | y >= 0. && y < 1.;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:45:5
+ |
+LL | y < 0. || y > 1.;
+ | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:48:5
+ |
+LL | x >= -10 && x <= 10;
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:50:5
+ |
+LL | y >= -3. && y <= 3.;
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:55:30
+ |
+LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:55:5
+ |
+LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:56:29
+ |
+LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:56:5
+ |
+LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
+
+error: aborting due to 20 previous errors
+
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.fixed b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed
new file mode 100644
index 000000000..40d7791df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(unused_parens)]
+#![allow(clippy::iter_with_drain)]
+fn f() -> usize {
+ 42
+}
+
+#[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
+fn main() {
+ for _ in 0..2 {}
+ for _ in 0..=2 {}
+
+ for _ in 0..=3 {}
+ for _ in 0..=3 + 1 {}
+
+ for _ in 0..=5 {}
+ for _ in 0..=1 + 5 {}
+
+ for _ in 1..=1 {}
+ for _ in 1..=1 + 1 {}
+
+ for _ in 0..13 + 13 {}
+ for _ in 0..=13 - 7 {}
+
+ for _ in 0..=f() {}
+ for _ in 0..=(1 + f()) {}
+
+ let _ = ..11 - 1;
+ let _ = ..11;
+ let _ = ..11;
+ let _ = (1..=11);
+ let _ = ((f() + 1)..=f());
+
+ const ONE: usize = 1;
+ // integer consts are linted, too
+ for _ in 1..=ONE {}
+
+ let mut vec: Vec<()> = std::vec::Vec::new();
+ vec.drain(..);
+}
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.rs b/src/tools/clippy/tests/ui/range_plus_minus_one.rs
new file mode 100644
index 000000000..a8ddd9b5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(unused_parens)]
+#![allow(clippy::iter_with_drain)]
+fn f() -> usize {
+ 42
+}
+
+#[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
+fn main() {
+ for _ in 0..2 {}
+ for _ in 0..=2 {}
+
+ for _ in 0..3 + 1 {}
+ for _ in 0..=3 + 1 {}
+
+ for _ in 0..1 + 5 {}
+ for _ in 0..=1 + 5 {}
+
+ for _ in 1..1 + 1 {}
+ for _ in 1..=1 + 1 {}
+
+ for _ in 0..13 + 13 {}
+ for _ in 0..=13 - 7 {}
+
+ for _ in 0..(1 + f()) {}
+ for _ in 0..=(1 + f()) {}
+
+ let _ = ..11 - 1;
+ let _ = ..=11 - 1;
+ let _ = ..=(11 - 1);
+ let _ = (1..11 + 1);
+ let _ = (f() + 1)..(f() + 1);
+
+ const ONE: usize = 1;
+ // integer consts are linted, too
+ for _ in 1..ONE + ONE {}
+
+ let mut vec: Vec<()> = std::vec::Vec::new();
+ vec.drain(..);
+}
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr
new file mode 100644
index 000000000..fb4f16585
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr
@@ -0,0 +1,60 @@
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:15:14
+ |
+LL | for _ in 0..3 + 1 {}
+ | ^^^^^^^^ help: use: `0..=3`
+ |
+ = note: `-D clippy::range-plus-one` implied by `-D warnings`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:18:14
+ |
+LL | for _ in 0..1 + 5 {}
+ | ^^^^^^^^ help: use: `0..=5`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:21:14
+ |
+LL | for _ in 1..1 + 1 {}
+ | ^^^^^^^^ help: use: `1..=1`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:27:14
+ |
+LL | for _ in 0..(1 + f()) {}
+ | ^^^^^^^^^^^^ help: use: `0..=f()`
+
+error: an exclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:31:13
+ |
+LL | let _ = ..=11 - 1;
+ | ^^^^^^^^^ help: use: `..11`
+ |
+ = note: `-D clippy::range-minus-one` implied by `-D warnings`
+
+error: an exclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:32:13
+ |
+LL | let _ = ..=(11 - 1);
+ | ^^^^^^^^^^^ help: use: `..11`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:33:13
+ |
+LL | let _ = (1..11 + 1);
+ | ^^^^^^^^^^^ help: use: `(1..=11)`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:34:13
+ |
+LL | let _ = (f() + 1)..(f() + 1);
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
+
+error: an inclusive range would be more readable
+ --> $DIR/range_plus_minus_one.rs:38:14
+ |
+LL | for _ in 1..ONE + ONE {}
+ | ^^^^^^^^^^^^ help: use: `1..=ONE`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_buffer.fixed b/src/tools/clippy/tests/ui/rc_buffer.fixed
new file mode 100644
index 000000000..8910c01b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer.fixed
@@ -0,0 +1,28 @@
+// run-rustfix
+#![warn(clippy::rc_buffer)]
+#![allow(dead_code, unused_imports)]
+
+use std::cell::RefCell;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+struct S {
+ // triggers lint
+ bad1: Rc<str>,
+ bad2: Rc<std::path::Path>,
+ bad3: Rc<[u8]>,
+ bad4: Rc<std::ffi::OsStr>,
+ // does not trigger lint
+ good1: Rc<RefCell<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Rc<str>) {}
+fn func_bad2(_: Rc<std::path::Path>) {}
+fn func_bad3(_: Rc<[u8]>) {}
+fn func_bad4(_: Rc<std::ffi::OsStr>) {}
+// does not trigger lint
+fn func_good1(_: Rc<RefCell<String>>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_buffer.rs b/src/tools/clippy/tests/ui/rc_buffer.rs
new file mode 100644
index 000000000..1e63a4326
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer.rs
@@ -0,0 +1,28 @@
+// run-rustfix
+#![warn(clippy::rc_buffer)]
+#![allow(dead_code, unused_imports)]
+
+use std::cell::RefCell;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+struct S {
+ // triggers lint
+ bad1: Rc<String>,
+ bad2: Rc<PathBuf>,
+ bad3: Rc<Vec<u8>>,
+ bad4: Rc<OsString>,
+ // does not trigger lint
+ good1: Rc<RefCell<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Rc<String>) {}
+fn func_bad2(_: Rc<PathBuf>) {}
+fn func_bad3(_: Rc<Vec<u8>>) {}
+fn func_bad4(_: Rc<OsString>) {}
+// does not trigger lint
+fn func_good1(_: Rc<RefCell<String>>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_buffer.stderr b/src/tools/clippy/tests/ui/rc_buffer.stderr
new file mode 100644
index 000000000..9ed028e3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer.stderr
@@ -0,0 +1,52 @@
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:12:11
+ |
+LL | bad1: Rc<String>,
+ | ^^^^^^^^^^ help: try: `Rc<str>`
+ |
+ = note: `-D clippy::rc-buffer` implied by `-D warnings`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:13:11
+ |
+LL | bad2: Rc<PathBuf>,
+ | ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:14:11
+ |
+LL | bad3: Rc<Vec<u8>>,
+ | ^^^^^^^^^^^ help: try: `Rc<[u8]>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:15:11
+ |
+LL | bad4: Rc<OsString>,
+ | ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:21:17
+ |
+LL | fn func_bad1(_: Rc<String>) {}
+ | ^^^^^^^^^^ help: try: `Rc<str>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:22:17
+ |
+LL | fn func_bad2(_: Rc<PathBuf>) {}
+ | ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:23:17
+ |
+LL | fn func_bad3(_: Rc<Vec<u8>>) {}
+ | ^^^^^^^^^^^ help: try: `Rc<[u8]>`
+
+error: usage of `Rc<T>` when T is a buffer type
+ --> $DIR/rc_buffer.rs:24:17
+ |
+LL | fn func_bad4(_: Rc<OsString>) {}
+ | ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.fixed b/src/tools/clippy/tests/ui/rc_buffer_arc.fixed
new file mode 100644
index 000000000..13dd6f5fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer_arc.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+#![warn(clippy::rc_buffer)]
+#![allow(dead_code, unused_imports)]
+
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::sync::{Arc, Mutex};
+
+struct S {
+ // triggers lint
+ bad1: Arc<str>,
+ bad2: Arc<std::path::Path>,
+ bad3: Arc<[u8]>,
+ bad4: Arc<std::ffi::OsStr>,
+ // does not trigger lint
+ good1: Arc<Mutex<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Arc<str>) {}
+fn func_bad2(_: Arc<std::path::Path>) {}
+fn func_bad3(_: Arc<[u8]>) {}
+fn func_bad4(_: Arc<std::ffi::OsStr>) {}
+// does not trigger lint
+fn func_good1(_: Arc<Mutex<String>>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.rs b/src/tools/clippy/tests/ui/rc_buffer_arc.rs
new file mode 100644
index 000000000..1a521bfeb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer_arc.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+#![warn(clippy::rc_buffer)]
+#![allow(dead_code, unused_imports)]
+
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::sync::{Arc, Mutex};
+
+struct S {
+ // triggers lint
+ bad1: Arc<String>,
+ bad2: Arc<PathBuf>,
+ bad3: Arc<Vec<u8>>,
+ bad4: Arc<OsString>,
+ // does not trigger lint
+ good1: Arc<Mutex<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Arc<String>) {}
+fn func_bad2(_: Arc<PathBuf>) {}
+fn func_bad3(_: Arc<Vec<u8>>) {}
+fn func_bad4(_: Arc<OsString>) {}
+// does not trigger lint
+fn func_good1(_: Arc<Mutex<String>>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.stderr b/src/tools/clippy/tests/ui/rc_buffer_arc.stderr
new file mode 100644
index 000000000..911feea73
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer_arc.stderr
@@ -0,0 +1,52 @@
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:11:11
+ |
+LL | bad1: Arc<String>,
+ | ^^^^^^^^^^^ help: try: `Arc<str>`
+ |
+ = note: `-D clippy::rc-buffer` implied by `-D warnings`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:12:11
+ |
+LL | bad2: Arc<PathBuf>,
+ | ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:13:11
+ |
+LL | bad3: Arc<Vec<u8>>,
+ | ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:14:11
+ |
+LL | bad4: Arc<OsString>,
+ | ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:20:17
+ |
+LL | fn func_bad1(_: Arc<String>) {}
+ | ^^^^^^^^^^^ help: try: `Arc<str>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:21:17
+ |
+LL | fn func_bad2(_: Arc<PathBuf>) {}
+ | ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:22:17
+ |
+LL | fn func_bad3(_: Arc<Vec<u8>>) {}
+ | ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
+
+error: usage of `Arc<T>` when T is a buffer type
+ --> $DIR/rc_buffer_arc.rs:23:17
+ |
+LL | fn func_bad4(_: Arc<OsString>) {}
+ | ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs b/src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs
new file mode 100644
index 000000000..5d31a848c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs
@@ -0,0 +1,12 @@
+#![warn(clippy::rc_buffer)]
+
+use std::rc::Rc;
+
+struct String;
+
+struct S {
+ // does not trigger lint
+ good1: Rc<String>,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs
new file mode 100644
index 000000000..384060e6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::rc_clone_in_vec_init)]
+use std::sync::{Arc, Mutex};
+
+fn main() {}
+
+fn should_warn_simple_case() {
+ let v = vec![Arc::new("x".to_string()); 2];
+}
+
+fn should_warn_simple_case_with_big_indentation() {
+ if true {
+ let k = 1;
+ dbg!(k);
+ if true {
+ let v = vec![Arc::new("x".to_string()); 2];
+ }
+ }
+}
+
+fn should_warn_complex_case() {
+ let v = vec![
+ std::sync::Arc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ }));
+ 2
+ ];
+
+ let v1 = vec![
+ Arc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ }));
+ 2
+ ];
+}
+
+fn should_not_warn_custom_arc() {
+ #[derive(Clone)]
+ struct Arc;
+
+ impl Arc {
+ fn new() -> Self {
+ Arc
+ }
+ }
+
+ let v = vec![Arc::new(); 2];
+}
+
+fn should_not_warn_vec_from_elem_but_not_arc() {
+ let v = vec![String::new(); 2];
+ let v1 = vec![1; 2];
+ let v2 = vec![
+ Box::new(std::sync::Arc::new({
+ let y = 3;
+ dbg!(y);
+ y
+ }));
+ 2
+ ];
+}
+
+fn should_not_warn_vec_macro_but_not_from_elem() {
+ let v = vec![Arc::new("x".to_string())];
+}
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
new file mode 100644
index 000000000..cd7d91e12
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
@@ -0,0 +1,109 @@
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/arc.rs:7:13
+ |
+LL | let v = vec![Arc::new("x".to_string()); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
+ = note: each element will point to the same `Arc` instance
+help: consider initializing each `Arc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Arc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Arc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/arc.rs:15:21
+ |
+LL | let v = vec![Arc::new("x".to_string()); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Arc` instance
+help: consider initializing each `Arc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Arc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Arc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/arc.rs:21:13
+ |
+LL | let v = vec![
+ | _____________^
+LL | | std::sync::Arc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Arc` instance
+help: consider initializing each `Arc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(std::sync::Arc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Arc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = std::sync::Arc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/arc.rs:30:14
+ |
+LL | let v1 = vec![
+ | ______________^
+LL | | Arc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Arc` instance
+help: consider initializing each `Arc` element individually
+ |
+LL ~ let v1 = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Arc` initialization to a variable
+ |
+LL ~ let v1 = {
+LL + let data = Arc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs
new file mode 100644
index 000000000..0394457fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.rs
@@ -0,0 +1,69 @@
+#![warn(clippy::rc_clone_in_vec_init)]
+use std::rc::Rc;
+use std::sync::Mutex;
+
+fn main() {}
+
+fn should_warn_simple_case() {
+ let v = vec![Rc::new("x".to_string()); 2];
+}
+
+fn should_warn_simple_case_with_big_indentation() {
+ if true {
+ let k = 1;
+ dbg!(k);
+ if true {
+ let v = vec![Rc::new("x".to_string()); 2];
+ }
+ }
+}
+
+fn should_warn_complex_case() {
+ let v = vec![
+ std::rc::Rc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ }));
+ 2
+ ];
+
+ let v1 = vec![
+ Rc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ }));
+ 2
+ ];
+}
+
+fn should_not_warn_custom_arc() {
+ #[derive(Clone)]
+ struct Rc;
+
+ impl Rc {
+ fn new() -> Self {
+ Rc
+ }
+ }
+
+ let v = vec![Rc::new(); 2];
+}
+
+fn should_not_warn_vec_from_elem_but_not_rc() {
+ let v = vec![String::new(); 2];
+ let v1 = vec![1; 2];
+ let v2 = vec![
+ Box::new(std::rc::Rc::new({
+ let y = 3;
+ dbg!(y);
+ y
+ }));
+ 2
+ ];
+}
+
+fn should_not_warn_vec_macro_but_not_from_elem() {
+ let v = vec![Rc::new("x".to_string())];
+}
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
new file mode 100644
index 000000000..fe861afe0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
@@ -0,0 +1,109 @@
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/rc.rs:8:13
+ |
+LL | let v = vec![Rc::new("x".to_string()); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
+ = note: each element will point to the same `Rc` instance
+help: consider initializing each `Rc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Rc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Rc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/rc.rs:16:21
+ |
+LL | let v = vec![Rc::new("x".to_string()); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Rc` instance
+help: consider initializing each `Rc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Rc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Rc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/rc.rs:22:13
+ |
+LL | let v = vec![
+ | _____________^
+LL | | std::rc::Rc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Rc` instance
+help: consider initializing each `Rc` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(std::rc::Rc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Rc` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = std::rc::Rc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/rc.rs:31:14
+ |
+LL | let v1 = vec![
+ | ______________^
+LL | | Rc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Rc` instance
+help: consider initializing each `Rc` element individually
+ |
+LL ~ let v1 = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Rc` initialization to a variable
+ |
+LL ~ let v1 = {
+LL + let data = Rc::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
new file mode 100644
index 000000000..693c9b553
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
@@ -0,0 +1,83 @@
+#![warn(clippy::rc_clone_in_vec_init)]
+use std::rc::{Rc, Weak as UnSyncWeak};
+use std::sync::{Arc, Mutex, Weak as SyncWeak};
+
+fn main() {}
+
+fn should_warn_simple_case() {
+ let v = vec![SyncWeak::<u32>::new(); 2];
+ let v2 = vec![UnSyncWeak::<u32>::new(); 2];
+
+ let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
+ let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
+}
+
+fn should_warn_simple_case_with_big_indentation() {
+ if true {
+ let k = 1;
+ dbg!(k);
+ if true {
+ let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
+ let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
+ }
+ }
+}
+
+fn should_warn_complex_case() {
+ let v = vec![
+ Arc::downgrade(&Arc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ })));
+ 2
+ ];
+
+ let v1 = vec![
+ Rc::downgrade(&Rc::new(Mutex::new({
+ let x = 1;
+ dbg!(x);
+ x
+ })));
+ 2
+ ];
+}
+
+fn should_not_warn_custom_weak() {
+ #[derive(Clone)]
+ struct Weak;
+
+ impl Weak {
+ fn new() -> Self {
+ Weak
+ }
+ }
+
+ let v = vec![Weak::new(); 2];
+}
+
+fn should_not_warn_vec_from_elem_but_not_weak() {
+ let v = vec![String::new(); 2];
+ let v1 = vec![1; 2];
+ let v2 = vec![
+ Box::new(Arc::downgrade(&Arc::new({
+ let y = 3;
+ dbg!(y);
+ y
+ })));
+ 2
+ ];
+ let v3 = vec![
+ Box::new(Rc::downgrade(&Rc::new({
+ let y = 3;
+ dbg!(y);
+ y
+ })));
+ 2
+ ];
+}
+
+fn should_not_warn_vec_macro_but_not_from_elem() {
+ let v = vec![Arc::downgrade(&Arc::new("x".to_string()))];
+ let v = vec![Rc::downgrade(&Rc::new("x".to_string()))];
+}
diff --git a/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
new file mode 100644
index 000000000..4a21946cc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
@@ -0,0 +1,201 @@
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:8:13
+ |
+LL | let v = vec![SyncWeak::<u32>::new(); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(SyncWeak::<u32>::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = SyncWeak::<u32>::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:9:14
+ |
+LL | let v2 = vec![UnSyncWeak::<u32>::new(); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v2 = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(UnSyncWeak::<u32>::new(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v2 = {
+LL + let data = UnSyncWeak::<u32>::new(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:11:13
+ |
+LL | let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Rc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:12:13
+ |
+LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Arc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:20:21
+ |
+LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Arc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:21:22
+ |
+LL | let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v2 = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v2 = {
+LL + let data = Rc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:27:13
+ |
+LL | let v = vec![
+ | _____________^
+LL | | Arc::downgrade(&Arc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Arc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v = {
+LL + let data = Arc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: initializing a reference-counted pointer in `vec![elem; len]`
+ --> $DIR/weak.rs:36:14
+ |
+LL | let v1 = vec![
+ | ______________^
+LL | | Rc::downgrade(&Rc::new(Mutex::new({
+LL | | let x = 1;
+LL | | dbg!(x);
+... |
+LL | | 2
+LL | | ];
+ | |_____^
+ |
+ = note: each element will point to the same `Weak` instance
+help: consider initializing each `Weak` element individually
+ |
+LL ~ let v1 = {
+LL + let mut v = Vec::with_capacity(2);
+LL + (0..2).for_each(|_| v.push(Rc::downgrade(..)));
+LL + v
+LL ~ };
+ |
+help: or if this is intentional, consider extracting the `Weak` initialization to a variable
+ |
+LL ~ let v1 = {
+LL + let data = Rc::downgrade(..);
+LL + vec![data; 2]
+LL ~ };
+ |
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rc_mutex.rs b/src/tools/clippy/tests/ui/rc_mutex.rs
new file mode 100644
index 000000000..18e8a2e01
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_mutex.rs
@@ -0,0 +1,36 @@
+#![warn(clippy::rc_mutex)]
+#![allow(unused, clippy::blacklisted_name)]
+
+use std::rc::Rc;
+use std::sync::Mutex;
+
+pub struct MyStructWithPrivItem {
+ foo: Rc<Mutex<i32>>,
+}
+
+pub struct MyStructWithPubItem {
+ pub foo: Rc<Mutex<i32>>,
+}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+// All of these test should be trigger the lint because they are not
+// part of the public api
+fn test1<T>(foo: Rc<Mutex<T>>) {}
+fn test2(foo: Rc<Mutex<MyEnum>>) {}
+fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test1<T>(foo: Rc<Mutex<T>>) {}
+pub fn pub_test2(foo: Rc<Mutex<MyEnum>>) {}
+pub fn pub_test3(foo: Rc<Mutex<SubT<usize>>>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_mutex.stderr b/src/tools/clippy/tests/ui/rc_mutex.stderr
new file mode 100644
index 000000000..fe84361d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rc_mutex.stderr
@@ -0,0 +1,35 @@
+error: usage of `Rc<Mutex<_>>`
+ --> $DIR/rc_mutex.rs:8:10
+ |
+LL | foo: Rc<Mutex<i32>>,
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rc-mutex` implied by `-D warnings`
+ = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+
+error: usage of `Rc<Mutex<_>>`
+ --> $DIR/rc_mutex.rs:26:18
+ |
+LL | fn test1<T>(foo: Rc<Mutex<T>>) {}
+ | ^^^^^^^^^^^^
+ |
+ = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+
+error: usage of `Rc<Mutex<_>>`
+ --> $DIR/rc_mutex.rs:27:15
+ |
+LL | fn test2(foo: Rc<Mutex<MyEnum>>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+
+error: usage of `Rc<Mutex<_>>`
+ --> $DIR/rc_mutex.rs:28:15
+ |
+LL | fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs
new file mode 100644
index 000000000..30807e0f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs
@@ -0,0 +1,87 @@
+#![warn(clippy::read_zero_byte_vec)]
+#![allow(clippy::unused_io_amount)]
+use std::fs::File;
+use std::io;
+use std::io::prelude::*;
+
+extern crate futures;
+use futures::io::{AsyncRead, AsyncReadExt};
+use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _};
+
+fn test() -> io::Result<()> {
+ let cap = 1000;
+ let mut f = File::open("foo.txt").unwrap();
+
+ // should lint
+ let mut data = Vec::with_capacity(20);
+ f.read_exact(&mut data).unwrap();
+
+ // should lint
+ let mut data2 = Vec::with_capacity(cap);
+ f.read_exact(&mut data2)?;
+
+ // should lint
+ let mut data3 = Vec::new();
+ f.read_exact(&mut data3)?;
+
+ // should lint
+ let mut data4 = vec![];
+ let _ = f.read(&mut data4)?;
+
+ // should lint
+ let _ = {
+ let mut data5 = Vec::new();
+ f.read(&mut data5)
+ };
+
+ // should lint
+ let _ = {
+ let mut data6: Vec<u8> = Default::default();
+ f.read(&mut data6)
+ };
+
+ // should not lint
+ let mut buf = [0u8; 100];
+ f.read(&mut buf)?;
+
+ // should not lint
+ let mut empty = vec![];
+ let mut data7 = vec![];
+ f.read(&mut empty);
+
+ // should not lint
+ f.read(&mut data7);
+
+ // should not lint
+ let mut data8 = Vec::new();
+ data8.resize(100, 0);
+ f.read_exact(&mut data8)?;
+
+ // should not lint
+ let mut data9 = vec![1, 2, 3];
+ f.read_exact(&mut data9)?;
+
+ Ok(())
+}
+
+async fn test_futures<R: AsyncRead + Unpin>(r: &mut R) {
+ // should lint
+ let mut data = Vec::new();
+ r.read(&mut data).await.unwrap();
+
+ // should lint
+ let mut data2 = Vec::new();
+ r.read_exact(&mut data2).await.unwrap();
+}
+
+async fn test_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) {
+ // should lint
+ let mut data = Vec::new();
+ r.read(&mut data).await.unwrap();
+
+ // should lint
+ let mut data2 = Vec::new();
+ r.read_exact(&mut data2).await.unwrap();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr
new file mode 100644
index 000000000..08ba9753d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr
@@ -0,0 +1,64 @@
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:17:5
+ |
+LL | f.read_exact(&mut data).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();`
+ |
+ = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings`
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:21:5
+ |
+LL | f.read_exact(&mut data2)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;`
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:25:5
+ |
+LL | f.read_exact(&mut data3)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:29:5
+ |
+LL | let _ = f.read(&mut data4)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:34:9
+ |
+LL | f.read(&mut data5)
+ | ^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:40:9
+ |
+LL | f.read(&mut data6)
+ | ^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:70:5
+ |
+LL | r.read(&mut data).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:74:5
+ |
+LL | r.read_exact(&mut data2).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:80:5
+ |
+LL | r.read(&mut data).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: reading zero byte data to `Vec`
+ --> $DIR/read_zero_byte_vec.rs:84:5
+ |
+LL | r.read_exact(&mut data2).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.rs b/src/tools/clippy/tests/ui/recursive_format_impl.rs
new file mode 100644
index 000000000..cb6ba36b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/recursive_format_impl.rs
@@ -0,0 +1,322 @@
+#![warn(clippy::recursive_format_impl)]
+#![allow(
+ clippy::inherent_to_string_shadow_display,
+ clippy::to_string_in_format_args,
+ clippy::deref_addrof,
+ clippy::borrow_deref_ref
+)]
+
+use std::fmt;
+
+struct A;
+impl A {
+ fn fmt(&self) {
+ self.to_string();
+ }
+}
+
+trait B {
+ fn fmt(&self) {}
+}
+
+impl B for A {
+ fn fmt(&self) {
+ self.to_string();
+ }
+}
+
+impl fmt::Display for A {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.to_string())
+ }
+}
+
+fn fmt(a: A) {
+ a.to_string();
+}
+
+struct C;
+
+impl C {
+ // Doesn't trigger if to_string defined separately
+ // i.e. not using ToString trait (from Display)
+ fn to_string(&self) -> String {
+ String::from("I am C")
+ }
+}
+
+impl fmt::Display for C {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.to_string())
+ }
+}
+
+enum D {
+ E(String),
+ F,
+}
+
+impl std::fmt::Display for D {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match &self {
+ Self::E(string) => write!(f, "E {}", string.to_string()),
+ Self::F => write!(f, "F"),
+ }
+ }
+}
+
+// Check for use of self as Display, in Display impl
+// Triggers on direct use of self
+struct G;
+
+impl std::fmt::Display for G {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
+// Triggers on reference to self
+struct H;
+
+impl std::fmt::Display for H {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", &self)
+ }
+}
+
+impl std::fmt::Debug for H {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", &self)
+ }
+}
+
+// Triggers on multiple reference to self
+struct H2;
+
+impl std::fmt::Display for H2 {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", &&&self)
+ }
+}
+
+// Doesn't trigger on correct deref
+struct I;
+
+impl std::ops::Deref for I {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for I {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", &**self)
+ }
+}
+
+impl std::fmt::Debug for I {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{:?}", &**self)
+ }
+}
+
+// Doesn't trigger on multiple correct deref
+struct I2;
+
+impl std::ops::Deref for I2 {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for I2 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", **&&&**self)
+ }
+}
+
+// Doesn't trigger on multiple correct deref
+struct I3;
+
+impl std::ops::Deref for I3 {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for I3 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", &&**&&&**self)
+ }
+}
+
+// Does trigger when deref resolves to self
+struct J;
+
+impl std::ops::Deref for J {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for J {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", &*self)
+ }
+}
+
+impl std::fmt::Debug for J {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{:?}", &*self)
+ }
+}
+
+struct J2;
+
+impl std::ops::Deref for J2 {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for J2 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", *self)
+ }
+}
+
+struct J3;
+
+impl std::ops::Deref for J3 {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for J3 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", **&&*self)
+ }
+}
+
+struct J4;
+
+impl std::ops::Deref for J4 {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "test"
+ }
+}
+
+impl std::fmt::Display for J4 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", &&**&&*self)
+ }
+}
+
+// Doesn't trigger on Debug from Display
+struct K;
+
+impl std::fmt::Debug for K {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "test")
+ }
+}
+
+impl std::fmt::Display for K {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+// Doesn't trigger on Display from Debug
+struct K2;
+
+impl std::fmt::Debug for K2 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
+impl std::fmt::Display for K2 {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "test")
+ }
+}
+
+// Doesn't trigger on struct fields
+struct L {
+ field1: u32,
+ field2: i32,
+}
+
+impl std::fmt::Display for L {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{},{}", self.field1, self.field2)
+ }
+}
+
+impl std::fmt::Debug for L {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{:?},{:?}", self.field1, self.field2)
+ }
+}
+
+// Doesn't trigger on nested enum matching
+enum Tree {
+ Leaf,
+ Node(Vec<Tree>),
+}
+
+impl std::fmt::Display for Tree {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Tree::Leaf => write!(f, "*"),
+ Tree::Node(children) => {
+ write!(f, "(")?;
+ for child in children.iter() {
+ write!(f, "{},", child)?;
+ }
+ write!(f, ")")
+ },
+ }
+ }
+}
+
+impl std::fmt::Debug for Tree {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Tree::Leaf => write!(f, "*"),
+ Tree::Node(children) => {
+ write!(f, "(")?;
+ for child in children.iter() {
+ write!(f, "{:?},", child)?;
+ }
+ write!(f, ")")
+ },
+ }
+ }
+}
+
+fn main() {
+ let a = A;
+ a.to_string();
+ a.fmt();
+ fmt(a);
+
+ let c = C;
+ c.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.stderr b/src/tools/clippy/tests/ui/recursive_format_impl.stderr
new file mode 100644
index 000000000..84ce69df5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/recursive_format_impl.stderr
@@ -0,0 +1,82 @@
+error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:30:25
+ |
+LL | write!(f, "{}", self.to_string())
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::recursive-format-impl` implied by `-D warnings`
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:74:9
+ |
+LL | write!(f, "{}", self)
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:83:9
+ |
+LL | write!(f, "{}", &self)
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:89:9
+ |
+LL | write!(f, "{:?}", &self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:98:9
+ |
+LL | write!(f, "{}", &&&self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:172:9
+ |
+LL | write!(f, "{}", &*self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:178:9
+ |
+LL | write!(f, "{:?}", &*self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:194:9
+ |
+LL | write!(f, "{}", *self)
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:210:9
+ |
+LL | write!(f, "{}", **&&*self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: using `self` as `Display` in `impl Display` will cause infinite recursion
+ --> $DIR/recursive_format_impl.rs:226:9
+ |
+LL | write!(f, "{}", &&**&&*self)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs
new file mode 100644
index 000000000..cf7d8c6e3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_allocation.rs
@@ -0,0 +1,135 @@
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct;
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+
+ pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+
+ pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ unimplemented!();
+ }
+
+ pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test5(a: Rc<Box<bool>>) {}
+
+ pub fn rc_test7(a: Rc<Arc<bool>>) {}
+
+ pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
+
+ pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test5(a: Arc<Box<bool>>) {}
+
+ pub fn arc_test6(a: Arc<Rc<bool>>) {}
+
+ pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
+
+ pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ unimplemented!();
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7487
+mod box_dyn {
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub trait T {}
+
+ struct S {
+ a: Box<Box<dyn T>>,
+ b: Rc<Box<dyn T>>,
+ c: Arc<Box<dyn T>>,
+ }
+
+ pub fn test_box(_: Box<Box<dyn T>>) {}
+ pub fn test_rc(_: Rc<Box<dyn T>>) {}
+ pub fn test_arc(_: Arc<Box<dyn T>>) {}
+ pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8604
+mod box_fat_ptr {
+ use std::boxed::Box;
+ use std::path::Path;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub struct DynSized {
+ foo: [usize],
+ }
+
+ struct S {
+ a: Box<Box<str>>,
+ b: Rc<Box<str>>,
+ c: Arc<Box<str>>,
+
+ e: Box<Box<[usize]>>,
+ f: Box<Box<Path>>,
+ g: Box<Box<DynSized>>,
+ }
+
+ pub fn test_box_str(_: Box<Box<str>>) {}
+ pub fn test_rc_str(_: Rc<Box<str>>) {}
+ pub fn test_arc_str(_: Arc<Box<str>>) {}
+
+ pub fn test_box_slice(_: Box<Box<[usize]>>) {}
+ pub fn test_box_path(_: Box<Box<Path>>) {}
+ pub fn test_box_custom(_: Box<Box<DynSized>>) {}
+
+ pub fn test_rc_box_str(_: Rc<Box<Box<str>>>) {}
+ pub fn test_rc_box_slice(_: Rc<Box<Box<[usize]>>>) {}
+ pub fn test_rc_box_path(_: Rc<Box<Box<Path>>>) {}
+ pub fn test_rc_box_custom(_: Rc<Box<Box<DynSized>>>) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr
new file mode 100644
index 000000000..fab1b069f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr
@@ -0,0 +1,183 @@
+error: usage of `Box<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:25:30
+ |
+LL | pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Rc<T>`
+
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:27:30
+ |
+LL | pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Rc<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:29:27
+ |
+LL | pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
+
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:33:30
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:33:46
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Rc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:46:24
+ |
+LL | pub fn rc_test5(a: Rc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Box<bool>`
+
+error: usage of `Rc<Arc<bool>>`
+ --> $DIR/redundant_allocation.rs:48:24
+ |
+LL | pub fn rc_test7(a: Rc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Arc<bool>`
+
+error: usage of `Rc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:50:26
+ |
+LL | pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Rc<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:54:29
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
+ = help: consider using just `Rc<T>` or `Arc<T>`
+
+error: usage of `Rc<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:54:44
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Arc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:67:25
+ |
+LL | pub fn arc_test5(a: Arc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Box<bool>`
+
+error: usage of `Arc<Rc<bool>>`
+ --> $DIR/redundant_allocation.rs:69:25
+ |
+LL | pub fn arc_test6(a: Arc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Rc<bool>`
+
+error: usage of `Arc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:71:27
+ |
+LL | pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Arc<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:75:30
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^
+ |
+ = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
+ = help: consider using just `Arc<T>` or `Rc<T>`
+
+error: usage of `Arc<Rc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:75:45
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
+
+error: usage of `Rc<Box<Box<dyn T>>>`
+ --> $DIR/redundant_allocation.rs:97:27
+ |
+LL | pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<Box<dyn T>>` is already on the heap, `Rc<Box<Box<dyn T>>>` makes an extra allocation
+ = help: consider using just `Rc<Box<dyn T>>` or `Box<Box<dyn T>>`
+
+error: usage of `Rc<Box<Box<str>>>`
+ --> $DIR/redundant_allocation.rs:129:31
+ |
+LL | pub fn test_rc_box_str(_: Rc<Box<Box<str>>>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<Box<str>>` is already on the heap, `Rc<Box<Box<str>>>` makes an extra allocation
+ = help: consider using just `Rc<Box<str>>` or `Box<Box<str>>`
+
+error: usage of `Rc<Box<Box<[usize]>>>`
+ --> $DIR/redundant_allocation.rs:130:33
+ |
+LL | pub fn test_rc_box_slice(_: Rc<Box<Box<[usize]>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<Box<[usize]>>` is already on the heap, `Rc<Box<Box<[usize]>>>` makes an extra allocation
+ = help: consider using just `Rc<Box<[usize]>>` or `Box<Box<[usize]>>`
+
+error: usage of `Rc<Box<Box<Path>>>`
+ --> $DIR/redundant_allocation.rs:131:32
+ |
+LL | pub fn test_rc_box_path(_: Rc<Box<Box<Path>>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<Box<Path>>` is already on the heap, `Rc<Box<Box<Path>>>` makes an extra allocation
+ = help: consider using just `Rc<Box<Path>>` or `Box<Box<Path>>`
+
+error: usage of `Rc<Box<Box<DynSized>>>`
+ --> $DIR/redundant_allocation.rs:132:34
+ |
+LL | pub fn test_rc_box_custom(_: Rc<Box<Box<DynSized>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<Box<DynSized>>` is already on the heap, `Rc<Box<Box<DynSized>>>` makes an extra allocation
+ = help: consider using just `Rc<Box<DynSized>>` or `Box<Box<DynSized>>`
+
+error: aborting due to 20 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed
new file mode 100644
index 000000000..e7ed84731
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct;
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: &T) {}
+
+ pub fn box_test2(foo: &MyStruct) {}
+
+ pub fn box_test3(foo: &MyEnum) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<T>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: &T) {}
+
+ pub fn rc_test2(foo: &MyStruct) {}
+
+ pub fn rc_test3(foo: &MyEnum) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<bool>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: &T) {}
+
+ pub fn arc_test2(foo: &MyStruct) {}
+
+ pub fn arc_test3(foo: &MyEnum) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<bool>) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs
new file mode 100644
index 000000000..de763f98b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct;
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: Box<&T>) {}
+
+ pub fn box_test2(foo: Box<&MyStruct>) {}
+
+ pub fn box_test3(foo: Box<&MyEnum>) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<Box<T>>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: Rc<&T>) {}
+
+ pub fn rc_test2(foo: Rc<&MyStruct>) {}
+
+ pub fn rc_test3(foo: Rc<&MyEnum>) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<Rc<bool>>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: Arc<&T>) {}
+
+ pub fn arc_test2(foo: Arc<&MyStruct>) {}
+
+ pub fn arc_test3(foo: Arc<&MyEnum>) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<Arc<bool>>) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr
new file mode 100644
index 000000000..fdd76ef17
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr
@@ -0,0 +1,99 @@
+error: usage of `Box<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:26:30
+ |
+LL | pub fn box_test1<T>(foo: Box<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `&T` is already a pointer, `Box<&T>` allocates a pointer on the heap
+
+error: usage of `Box<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:28:27
+ |
+LL | pub fn box_test2(foo: Box<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Box<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:30:27
+ |
+LL | pub fn box_test3(foo: Box<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Box<Box<T>>`
+ --> $DIR/redundant_allocation_fixable.rs:34:30
+ |
+LL | pub fn box_test5<T>(foo: Box<Box<T>>) {}
+ | ^^^^^^^^^^^ help: try: `Box<T>`
+ |
+ = note: `Box<T>` is already on the heap, `Box<Box<T>>` makes an extra allocation
+
+error: usage of `Rc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:45:29
+ |
+LL | pub fn rc_test1<T>(foo: Rc<&T>) {}
+ | ^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:47:26
+ |
+LL | pub fn rc_test2(foo: Rc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:49:26
+ |
+LL | pub fn rc_test3(foo: Rc<&MyEnum>) {}
+ | ^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Rc<Rc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:53:24
+ |
+LL | pub fn rc_test6(a: Rc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^ help: try: `Rc<bool>`
+ |
+ = note: `Rc<bool>` is already on the heap, `Rc<Rc<bool>>` makes an extra allocation
+
+error: usage of `Arc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:64:30
+ |
+LL | pub fn arc_test1<T>(foo: Arc<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:66:27
+ |
+LL | pub fn arc_test2(foo: Arc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:68:27
+ |
+LL | pub fn arc_test3(foo: Arc<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Arc<Arc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:72:25
+ |
+LL | pub fn arc_test7(a: Arc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^^ help: try: `Arc<bool>`
+ |
+ = note: `Arc<bool>` is already on the heap, `Arc<Arc<bool>>` makes an extra allocation
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed
new file mode 100644
index 000000000..da52c0acf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_clone.fixed
@@ -0,0 +1,241 @@
+// run-rustfix
+// rustfix-only-machine-applicable
+
+#![feature(lint_reasons)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
+use std::ffi::OsString;
+use std::path::Path;
+
+fn main() {
+ let _s = ["lorem", "ipsum"].join(" ");
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let s = String::from("foo");
+ let _s = s;
+
+ let _s = Path::new("/a/b/").join("c");
+
+ let _s = Path::new("/a/b/").join("c");
+
+ let _s = OsString::new();
+
+ let _s = OsString::new();
+
+ // Check that lint level works
+ #[allow(clippy::redundant_clone)]
+ let _s = String::new().to_string();
+
+ // Check that lint level works
+ #[expect(clippy::redundant_clone)]
+ let _s = String::new().to_string();
+
+ let tup = (String::from("foo"),);
+ let _t = tup.0;
+
+ let tup_ref = &(String::from("foo"),);
+ let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
+
+ {
+ let x = String::new();
+ let y = &x;
+
+ let _x = x.clone(); // ok; `x` is borrowed by `y`
+
+ let _ = y.len();
+ }
+
+ let x = (String::new(),);
+ let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
+
+ with_branch(Alpha, true);
+ cannot_double_move(Alpha);
+ cannot_move_from_type_with_drop();
+ borrower_propagation();
+ not_consumed();
+ issue_5405();
+ manually_drop();
+ clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
+}
+
+#[derive(Clone)]
+struct Alpha;
+fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
+ if b { (a.clone(), a) } else { (Alpha, a) }
+}
+
+fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
+ (a.clone(), a)
+}
+
+struct TypeWithDrop {
+ x: String,
+}
+
+impl Drop for TypeWithDrop {
+ fn drop(&mut self) {}
+}
+
+fn cannot_move_from_type_with_drop() -> String {
+ let s = TypeWithDrop { x: String::new() };
+ s.x.clone() // removing this `clone()` summons E0509
+}
+
+fn borrower_propagation() {
+ let s = String::new();
+ let t = String::new();
+
+ {
+ fn b() -> bool {
+ unimplemented!()
+ }
+ let _u = if b() { &s } else { &t };
+
+ // ok; `s` and `t` are possibly borrowed
+ let _s = s.clone();
+ let _t = t.clone();
+ }
+
+ {
+ let _u = || s.len();
+ let _v = [&t; 32];
+ let _s = s.clone(); // ok
+ let _t = t.clone(); // ok
+ }
+
+ {
+ let _u = {
+ let u = Some(&s);
+ let _ = s.clone(); // ok
+ u
+ };
+ let _s = s.clone(); // ok
+ }
+
+ {
+ use std::convert::identity as id;
+ let _u = id(id(&s));
+ let _s = s.clone(); // ok, `u` borrows `s`
+ }
+
+ let _s = s;
+ let _t = t;
+
+ #[derive(Clone)]
+ struct Foo {
+ x: usize,
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = Some(f.x);
+ let _f = f;
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = &f.x;
+ let _f = f.clone(); // ok
+ }
+}
+
+fn not_consumed() {
+ let x = std::path::PathBuf::from("home");
+ let y = x.join("matthias");
+ // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
+ // redundant. (It also does not consume the PathBuf)
+
+ println!("x: {:?}, y: {:?}", x, y);
+
+ let mut s = String::new();
+ s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
+ s.push_str("bar");
+ assert_eq!(s, "bar");
+
+ let t = Some(s);
+ // OK
+ if let Some(x) = t.clone() {
+ println!("{}", x);
+ }
+ if let Some(x) = t {
+ println!("{}", x);
+ }
+}
+
+#[allow(clippy::clone_on_copy)]
+fn issue_5405() {
+ let a: [String; 1] = [String::from("foo")];
+ let _b: String = a[0].clone();
+
+ let c: [usize; 2] = [2, 3];
+ let _d: usize = c[1].clone();
+}
+
+fn manually_drop() {
+ use std::mem::ManuallyDrop;
+ use std::sync::Arc;
+
+ let a = ManuallyDrop::new(Arc::new("Hello!".to_owned()));
+ let _ = a.clone(); // OK
+
+ let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a));
+ unsafe {
+ Arc::from_raw(p);
+ Arc::from_raw(p);
+ }
+}
+
+fn clone_then_move_cloned() {
+ // issue #5973
+ let x = Some(String::new());
+ // ok, x is moved while the clone is in use.
+ assert_eq!(x.clone(), None, "not equal {}", x.unwrap());
+
+ // issue #5595
+ fn foo<F: Fn()>(_: &Alpha, _: F) {}
+ let x = Alpha;
+ // ok, data is moved while the clone is in use.
+ foo(&x, move || {
+ let _ = x;
+ });
+
+ // issue #6998
+ struct S(String);
+ impl S {
+ fn m(&mut self) {}
+ }
+ let mut x = S(String::new());
+ x.0.clone().chars().for_each(|_| x.m());
+}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs
new file mode 100644
index 000000000..5867d019d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_clone.rs
@@ -0,0 +1,241 @@
+// run-rustfix
+// rustfix-only-machine-applicable
+
+#![feature(lint_reasons)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
+use std::ffi::OsString;
+use std::path::Path;
+
+fn main() {
+ let _s = ["lorem", "ipsum"].join(" ").to_string();
+
+ let s = String::from("foo");
+ let _s = s.clone();
+
+ let s = String::from("foo");
+ let _s = s.to_string();
+
+ let s = String::from("foo");
+ let _s = s.to_owned();
+
+ let _s = Path::new("/a/b/").join("c").to_owned();
+
+ let _s = Path::new("/a/b/").join("c").to_path_buf();
+
+ let _s = OsString::new().to_owned();
+
+ let _s = OsString::new().to_os_string();
+
+ // Check that lint level works
+ #[allow(clippy::redundant_clone)]
+ let _s = String::new().to_string();
+
+ // Check that lint level works
+ #[expect(clippy::redundant_clone)]
+ let _s = String::new().to_string();
+
+ let tup = (String::from("foo"),);
+ let _t = tup.0.clone();
+
+ let tup_ref = &(String::from("foo"),);
+ let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
+
+ {
+ let x = String::new();
+ let y = &x;
+
+ let _x = x.clone(); // ok; `x` is borrowed by `y`
+
+ let _ = y.len();
+ }
+
+ let x = (String::new(),);
+ let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
+
+ with_branch(Alpha, true);
+ cannot_double_move(Alpha);
+ cannot_move_from_type_with_drop();
+ borrower_propagation();
+ not_consumed();
+ issue_5405();
+ manually_drop();
+ clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
+}
+
+#[derive(Clone)]
+struct Alpha;
+fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
+ if b { (a.clone(), a.clone()) } else { (Alpha, a) }
+}
+
+fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
+ (a.clone(), a)
+}
+
+struct TypeWithDrop {
+ x: String,
+}
+
+impl Drop for TypeWithDrop {
+ fn drop(&mut self) {}
+}
+
+fn cannot_move_from_type_with_drop() -> String {
+ let s = TypeWithDrop { x: String::new() };
+ s.x.clone() // removing this `clone()` summons E0509
+}
+
+fn borrower_propagation() {
+ let s = String::new();
+ let t = String::new();
+
+ {
+ fn b() -> bool {
+ unimplemented!()
+ }
+ let _u = if b() { &s } else { &t };
+
+ // ok; `s` and `t` are possibly borrowed
+ let _s = s.clone();
+ let _t = t.clone();
+ }
+
+ {
+ let _u = || s.len();
+ let _v = [&t; 32];
+ let _s = s.clone(); // ok
+ let _t = t.clone(); // ok
+ }
+
+ {
+ let _u = {
+ let u = Some(&s);
+ let _ = s.clone(); // ok
+ u
+ };
+ let _s = s.clone(); // ok
+ }
+
+ {
+ use std::convert::identity as id;
+ let _u = id(id(&s));
+ let _s = s.clone(); // ok, `u` borrows `s`
+ }
+
+ let _s = s.clone();
+ let _t = t.clone();
+
+ #[derive(Clone)]
+ struct Foo {
+ x: usize,
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = Some(f.x);
+ let _f = f.clone();
+ }
+
+ {
+ let f = Foo { x: 123 };
+ let _x = &f.x;
+ let _f = f.clone(); // ok
+ }
+}
+
+fn not_consumed() {
+ let x = std::path::PathBuf::from("home");
+ let y = x.clone().join("matthias");
+ // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
+ // redundant. (It also does not consume the PathBuf)
+
+ println!("x: {:?}, y: {:?}", x, y);
+
+ let mut s = String::new();
+ s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
+ s.push_str("bar");
+ assert_eq!(s, "bar");
+
+ let t = Some(s);
+ // OK
+ if let Some(x) = t.clone() {
+ println!("{}", x);
+ }
+ if let Some(x) = t {
+ println!("{}", x);
+ }
+}
+
+#[allow(clippy::clone_on_copy)]
+fn issue_5405() {
+ let a: [String; 1] = [String::from("foo")];
+ let _b: String = a[0].clone();
+
+ let c: [usize; 2] = [2, 3];
+ let _d: usize = c[1].clone();
+}
+
+fn manually_drop() {
+ use std::mem::ManuallyDrop;
+ use std::sync::Arc;
+
+ let a = ManuallyDrop::new(Arc::new("Hello!".to_owned()));
+ let _ = a.clone(); // OK
+
+ let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a));
+ unsafe {
+ Arc::from_raw(p);
+ Arc::from_raw(p);
+ }
+}
+
+fn clone_then_move_cloned() {
+ // issue #5973
+ let x = Some(String::new());
+ // ok, x is moved while the clone is in use.
+ assert_eq!(x.clone(), None, "not equal {}", x.unwrap());
+
+ // issue #5595
+ fn foo<F: Fn()>(_: &Alpha, _: F) {}
+ let x = Alpha;
+ // ok, data is moved while the clone is in use.
+ foo(&x.clone(), move || {
+ let _ = x;
+ });
+
+ // issue #6998
+ struct S(String);
+ impl S {
+ fn m(&mut self) {}
+ }
+ let mut x = S(String::new());
+ x.0.clone().chars().for_each(|_| x.m());
+}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr
new file mode 100644
index 000000000..aa1dd7cbb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_clone.stderr
@@ -0,0 +1,183 @@
+error: redundant clone
+ --> $DIR/redundant_clone.rs:10:42
+ |
+LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
+ | ^^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::redundant-clone` implied by `-D warnings`
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:10:14
+ |
+LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:13:15
+ |
+LL | let _s = s.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:13:14
+ |
+LL | let _s = s.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:16:15
+ |
+LL | let _s = s.to_string();
+ | ^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:16:14
+ |
+LL | let _s = s.to_string();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:19:15
+ |
+LL | let _s = s.to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:19:14
+ |
+LL | let _s = s.to_owned();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:21:42
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:21:14
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_owned();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:23:42
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
+ | ^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:23:14
+ |
+LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:25:29
+ |
+LL | let _s = OsString::new().to_owned();
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:25:14
+ |
+LL | let _s = OsString::new().to_owned();
+ | ^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:27:29
+ |
+LL | let _s = OsString::new().to_os_string();
+ | ^^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:27:14
+ |
+LL | let _s = OsString::new().to_os_string();
+ | ^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:38:19
+ |
+LL | let _t = tup.0.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:38:14
+ |
+LL | let _t = tup.0.clone();
+ | ^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:70:25
+ |
+LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:70:24
+ |
+LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:127:15
+ |
+LL | let _s = s.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:127:14
+ |
+LL | let _s = s.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:128:15
+ |
+LL | let _t = t.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:128:14
+ |
+LL | let _t = t.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:138:19
+ |
+LL | let _f = f.clone();
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:138:18
+ |
+LL | let _f = f.clone();
+ | ^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:150:14
+ |
+LL | let y = x.clone().join("matthias");
+ | ^^^^^^^^ help: remove this
+ |
+note: cloned value is neither consumed nor mutated
+ --> $DIR/redundant_clone.rs:150:13
+ |
+LL | let y = x.clone().join("matthias");
+ | ^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/redundant_clone.rs:204:11
+ |
+LL | foo(&x.clone(), move || {
+ | ^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/redundant_clone.rs:204:10
+ |
+LL | foo(&x.clone(), move || {
+ | ^
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_early.rs b/src/tools/clippy/tests/ui/redundant_closure_call_early.rs
new file mode 100644
index 000000000..5649d8dd1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_early.rs
@@ -0,0 +1,20 @@
+// non rustfixable, see redundant_closure_call_fixable.rs
+
+#![warn(clippy::redundant_closure_call)]
+
+fn main() {
+ let mut i = 1;
+
+ // lint here
+ let mut k = (|m| m + 1)(i);
+
+ // lint here
+ k = (|a, b| a * b)(1, 5);
+
+ // don't lint these
+ #[allow(clippy::needless_return)]
+ (|| return 2)();
+ (|| -> Option<i32> { None? })();
+ #[allow(clippy::try_err)]
+ (|| -> Result<i32, i32> { Err(2)? })();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr
new file mode 100644
index 000000000..2735e4173
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr
@@ -0,0 +1,16 @@
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_early.rs:9:17
+ |
+LL | let mut k = (|m| m + 1)(i);
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_early.rs:12:9
+ |
+LL | k = (|a, b| a * b)(1, 5);
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
new file mode 100644
index 000000000..0abca6fca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+
+#![warn(clippy::redundant_closure_call)]
+#![allow(unused)]
+
+fn main() {
+ let a = 42;
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
new file mode 100644
index 000000000..f8b9d37a5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+
+#![warn(clippy::redundant_closure_call)]
+#![allow(unused)]
+
+fn main() {
+ let a = (|| 42)();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
new file mode 100644
index 000000000..afd704ef1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -0,0 +1,10 @@
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:7:13
+ |
+LL | let a = (|| 42)();
+ | ^^^^^^^^^ help: try doing something like: `42`
+ |
+ = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.rs b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs
new file mode 100644
index 000000000..5612827bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs
@@ -0,0 +1,40 @@
+// non rustfixable, see redundant_closure_call_fixable.rs
+
+#![warn(clippy::redundant_closure_call)]
+#![allow(clippy::needless_late_init)]
+
+fn main() {
+ let mut i = 1;
+
+ // don't lint here, the closure is used more than once
+ let closure = |i| i + 1;
+ i = closure(3);
+ i = closure(4);
+
+ // lint here
+ let redun_closure = || 1;
+ i = redun_closure();
+
+ // shadowed closures are supported, lint here
+ let shadowed_closure = || 1;
+ i = shadowed_closure();
+ let shadowed_closure = || 2;
+ i = shadowed_closure();
+
+ // don't lint here
+ let shadowed_closure = || 2;
+ i = shadowed_closure();
+ i = shadowed_closure();
+
+ // Fix FP in #5916
+ let mut x;
+ let create = || 2 * 2;
+ x = create();
+ fun(move || {
+ x = create();
+ })
+}
+
+fn fun<T: 'static + FnMut()>(mut f: T) {
+ f();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
new file mode 100644
index 000000000..4eca43a2b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
@@ -0,0 +1,22 @@
+error: closure called just once immediately after it was declared
+ --> $DIR/redundant_closure_call_late.rs:16:5
+ |
+LL | i = redun_closure();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
+
+error: closure called just once immediately after it was declared
+ --> $DIR/redundant_closure_call_late.rs:20:5
+ |
+LL | i = shadowed_closure();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: closure called just once immediately after it was declared
+ --> $DIR/redundant_closure_call_late.rs:22:5
+ |
+LL | i = shadowed_closure();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_else.rs b/src/tools/clippy/tests/ui/redundant_else.rs
new file mode 100644
index 000000000..64f566735
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_else.rs
@@ -0,0 +1,154 @@
+#![warn(clippy::redundant_else)]
+#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
+
+fn main() {
+ loop {
+ // break
+ if foo() {
+ println!("Love your neighbor;");
+ break;
+ } else {
+ println!("yet don't pull down your hedge.");
+ }
+ // continue
+ if foo() {
+ println!("He that lies down with Dogs,");
+ continue;
+ } else {
+ println!("shall rise up with fleas.");
+ }
+ // match block
+ if foo() {
+ match foo() {
+ 1 => break,
+ _ => return,
+ }
+ } else {
+ println!("You may delay, but time will not.");
+ }
+ }
+ // else if
+ if foo() {
+ return;
+ } else if foo() {
+ return;
+ } else {
+ println!("A fat kitchen makes a lean will.");
+ }
+ // let binding outside of block
+ let _ = {
+ if foo() {
+ return;
+ } else {
+ 1
+ }
+ };
+ // else if with let binding outside of block
+ let _ = {
+ if foo() {
+ return;
+ } else if foo() {
+ return;
+ } else {
+ 2
+ }
+ };
+ // inside if let
+ let _ = if let Some(1) = foo() {
+ let _ = 1;
+ if foo() {
+ return;
+ } else {
+ 1
+ }
+ } else {
+ 1
+ };
+
+ //
+ // non-lint cases
+ //
+
+ // sanity check
+ if foo() {
+ let _ = 1;
+ } else {
+ println!("Who is wise? He that learns from every one.");
+ }
+ // else if without else
+ if foo() {
+ return;
+ } else if foo() {
+ foo()
+ };
+ // nested if return
+ if foo() {
+ if foo() {
+ return;
+ }
+ } else {
+ foo()
+ };
+ // match with non-breaking branch
+ if foo() {
+ match foo() {
+ 1 => foo(),
+ _ => return,
+ }
+ } else {
+ println!("Three may keep a secret, if two of them are dead.");
+ }
+ // let binding
+ let _ = if foo() {
+ return;
+ } else {
+ 1
+ };
+ // assign
+ let mut a;
+ a = if foo() {
+ return;
+ } else {
+ 1
+ };
+ // assign-op
+ a += if foo() {
+ return;
+ } else {
+ 1
+ };
+ // if return else if else
+ if foo() {
+ return;
+ } else if foo() {
+ 1
+ } else {
+ 2
+ };
+ // if else if return else
+ if foo() {
+ 1
+ } else if foo() {
+ return;
+ } else {
+ 2
+ };
+ // else if with let binding
+ let _ = if foo() {
+ return;
+ } else if foo() {
+ return;
+ } else {
+ 2
+ };
+ // inside function call
+ Box::new(if foo() {
+ return;
+ } else {
+ 1
+ });
+}
+
+fn foo<T>() -> T {
+ unimplemented!("I'm not Santa Claus")
+}
diff --git a/src/tools/clippy/tests/ui/redundant_else.stderr b/src/tools/clippy/tests/ui/redundant_else.stderr
new file mode 100644
index 000000000..9000cdc81
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_else.stderr
@@ -0,0 +1,80 @@
+error: redundant else block
+ --> $DIR/redundant_else.rs:10:16
+ |
+LL | } else {
+ | ________________^
+LL | | println!("yet don't pull down your hedge.");
+LL | | }
+ | |_________^
+ |
+ = note: `-D clippy::redundant-else` implied by `-D warnings`
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:17:16
+ |
+LL | } else {
+ | ________________^
+LL | | println!("shall rise up with fleas.");
+LL | | }
+ | |_________^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:26:16
+ |
+LL | } else {
+ | ________________^
+LL | | println!("You may delay, but time will not.");
+LL | | }
+ | |_________^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:35:12
+ |
+LL | } else {
+ | ____________^
+LL | | println!("A fat kitchen makes a lean will.");
+LL | | }
+ | |_____^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:42:16
+ |
+LL | } else {
+ | ________________^
+LL | | 1
+LL | | }
+ | |_________^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:52:16
+ |
+LL | } else {
+ | ________________^
+LL | | 2
+LL | | }
+ | |_________^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: redundant else block
+ --> $DIR/redundant_else.rs:61:16
+ |
+LL | } else {
+ | ________________^
+LL | | 1
+LL | | }
+ | |_________^
+ |
+ = help: remove the `else` block and move the contents out
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed
new file mode 100644
index 000000000..5b4b8eeed
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed
@@ -0,0 +1,71 @@
+// run-rustfix
+#![warn(clippy::redundant_field_names)]
+#![allow(clippy::no_effect, dead_code, unused_variables)]
+
+#[macro_use]
+extern crate derive_new;
+
+use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
+
+mod foo {
+ pub const BAR: u8 = 0;
+}
+
+struct Person {
+ gender: u8,
+ age: u8,
+ name: u8,
+ buzz: u64,
+ foo: u8,
+}
+
+#[derive(new)]
+pub struct S {
+ v: String,
+}
+
+fn main() {
+ let gender: u8 = 42;
+ let age = 0;
+ let fizz: u64 = 0;
+ let name: u8 = 0;
+
+ let me = Person {
+ gender,
+ age,
+
+ name, //should be ok
+ buzz: fizz, //should be ok
+ foo: foo::BAR, //should be ok
+ };
+
+ // Range expressions
+ let (start, end) = (0, 0);
+
+ let _ = start..;
+ let _ = ..end;
+ let _ = start..end;
+
+ let _ = ..=end;
+ let _ = start..=end;
+
+ // Issue #2799
+ let _: Vec<_> = (start..end).collect();
+
+ // hand-written Range family structs are linted
+ let _ = RangeFrom { start };
+ let _ = RangeTo { end };
+ let _ = Range { start, end };
+ let _ = RangeInclusive::new(start, end);
+ let _ = RangeToInclusive { end };
+}
+
+fn issue_3476() {
+ fn foo<T>() {}
+
+ struct S {
+ foo: fn(),
+ }
+
+ S { foo: foo::<i32> };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs
new file mode 100644
index 000000000..3f97b80c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_field_names.rs
@@ -0,0 +1,71 @@
+// run-rustfix
+#![warn(clippy::redundant_field_names)]
+#![allow(clippy::no_effect, dead_code, unused_variables)]
+
+#[macro_use]
+extern crate derive_new;
+
+use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
+
+mod foo {
+ pub const BAR: u8 = 0;
+}
+
+struct Person {
+ gender: u8,
+ age: u8,
+ name: u8,
+ buzz: u64,
+ foo: u8,
+}
+
+#[derive(new)]
+pub struct S {
+ v: String,
+}
+
+fn main() {
+ let gender: u8 = 42;
+ let age = 0;
+ let fizz: u64 = 0;
+ let name: u8 = 0;
+
+ let me = Person {
+ gender: gender,
+ age: age,
+
+ name, //should be ok
+ buzz: fizz, //should be ok
+ foo: foo::BAR, //should be ok
+ };
+
+ // Range expressions
+ let (start, end) = (0, 0);
+
+ let _ = start..;
+ let _ = ..end;
+ let _ = start..end;
+
+ let _ = ..=end;
+ let _ = start..=end;
+
+ // Issue #2799
+ let _: Vec<_> = (start..end).collect();
+
+ // hand-written Range family structs are linted
+ let _ = RangeFrom { start: start };
+ let _ = RangeTo { end: end };
+ let _ = Range { start: start, end: end };
+ let _ = RangeInclusive::new(start, end);
+ let _ = RangeToInclusive { end: end };
+}
+
+fn issue_3476() {
+ fn foo<T>() {}
+
+ struct S {
+ foo: fn(),
+ }
+
+ S { foo: foo::<i32> };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_field_names.stderr b/src/tools/clippy/tests/ui/redundant_field_names.stderr
new file mode 100644
index 000000000..7976292df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_field_names.stderr
@@ -0,0 +1,46 @@
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:34:9
+ |
+LL | gender: gender,
+ | ^^^^^^^^^^^^^^ help: replace it with: `gender`
+ |
+ = note: `-D clippy::redundant-field-names` implied by `-D warnings`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:35:9
+ |
+LL | age: age,
+ | ^^^^^^^^ help: replace it with: `age`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:56:25
+ |
+LL | let _ = RangeFrom { start: start };
+ | ^^^^^^^^^^^^ help: replace it with: `start`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:57:23
+ |
+LL | let _ = RangeTo { end: end };
+ | ^^^^^^^^ help: replace it with: `end`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:58:21
+ |
+LL | let _ = Range { start: start, end: end };
+ | ^^^^^^^^^^^^ help: replace it with: `start`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:58:35
+ |
+LL | let _ = Range { start: start, end: end };
+ | ^^^^^^^^ help: replace it with: `end`
+
+error: redundant field names in struct initialization
+ --> $DIR/redundant_field_names.rs:60:32
+ |
+LL | let _ = RangeToInclusive { end: end };
+ | ^^^^^^^^ help: replace it with: `end`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
new file mode 100644
index 000000000..ce3229f17
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if m.lock().is_ok() {}
+ if Err::<(), _>(m.lock().unwrap().0).is_err() {}
+
+ {
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {
+ } else {
+ }
+ if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+ if Err::<std::sync::MutexGuard<()>, _>(()).is_err() {}
+
+ if Ok::<_, ()>(String::new()).is_ok() {}
+ if Err::<(), _>((String::new(), ())).is_err() {}
+
+ // Option
+ if Some(m.lock()).is_some() {}
+ if Some(m.lock().unwrap().0).is_some() {}
+
+ {
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+ }
+ if None::<std::sync::MutexGuard<()>>.is_none() {
+ } else {
+ }
+
+ if None::<std::sync::MutexGuard<()>>.is_none() {}
+
+ if Some(String::new()).is_some() {}
+ if Some((String::new(), ())).is_some() {}
+
+ // Poll
+ if Ready(m.lock()).is_ready() {}
+ if Ready(m.lock().unwrap().0).is_ready() {}
+
+ {
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+ }
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {
+ } else {
+ }
+
+ if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+
+ if Ready(String::new()).is_ready() {}
+ if Ready((String::new(), ())).is_ready() {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
new file mode 100644
index 000000000..29b8543cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+ let m = std::sync::Mutex::new((0, 0));
+
+ // Result
+ if let Ok(_) = m.lock() {}
+ if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+
+ {
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+ } else {
+ }
+ if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+
+ if let Ok(_) = Ok::<_, ()>(String::new()) {}
+ if let Err(_) = Err::<(), _>((String::new(), ())) {}
+
+ // Option
+ if let Some(_) = Some(m.lock()) {}
+ if let Some(_) = Some(m.lock().unwrap().0) {}
+
+ {
+ if let None = None::<std::sync::MutexGuard<()>> {}
+ }
+ if let None = None::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let None = None::<std::sync::MutexGuard<()>> {}
+
+ if let Some(_) = Some(String::new()) {}
+ if let Some(_) = Some((String::new(), ())) {}
+
+ // Poll
+ if let Ready(_) = Ready(m.lock()) {}
+ if let Ready(_) = Ready(m.lock().unwrap().0) {}
+
+ {
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ }
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {
+ } else {
+ }
+
+ if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+
+ if let Ready(_) = Ready(String::new()) {}
+ if let Ready(_) = Ready((String::new(), ())) {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
new file mode 100644
index 000000000..eb7aa70ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
@@ -0,0 +1,171 @@
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:12:12
+ |
+LL | if let Ok(_) = m.lock() {}
+ | -------^^^^^----------- help: try this: `if m.lock().is_ok()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:13:12
+ |
+LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+ | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:16:16
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:21:12
+ |
+LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+ | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:22:12
+ |
+LL | if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+ | -------^^^^^^------------------------------------------ help: try this: `if Err::<std::sync::MutexGuard<()>, _>(()).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:24:12
+ |
+LL | if let Ok(_) = Ok::<_, ()>(String::new()) {}
+ | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:25:12
+ |
+LL | if let Err(_) = Err::<(), _>((String::new(), ())) {}
+ | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:28:12
+ |
+LL | if let Some(_) = Some(m.lock()) {}
+ | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
+ |
+LL | if let Some(_) = Some(m.lock().unwrap().0) {}
+ | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:32:16
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {}
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:38:12
+ |
+LL | if let None = None::<std::sync::MutexGuard<()>> {}
+ | -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:40:12
+ |
+LL | if let Some(_) = Some(String::new()) {}
+ | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:41:12
+ |
+LL | if let Some(_) = Some((String::new(), ())) {}
+ | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:44:12
+ |
+LL | if let Ready(_) = Ready(m.lock()) {}
+ | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
+ |
+LL | if let Ready(_) = Ready(m.lock().unwrap().0) {}
+ | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:48:16
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+ |
+ = note: this will change drop order of the result, as well as all temporaries
+ = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:54:12
+ |
+LL | if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+ | -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:56:12
+ |
+LL | if let Ready(_) = Ready(String::new()) {}
+ | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_drop_order.rs:57:12
+ |
+LL | if let Ready(_) = Ready((String::new(), ())) {}
+ | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed
new file mode 100644
index 000000000..acc8de5f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+
+use std::net::{
+ IpAddr::{self, V4, V6},
+ Ipv4Addr, Ipv6Addr,
+};
+
+fn main() {
+ let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST);
+ if ipaddr.is_ipv4() {}
+
+ if V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ if V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ while V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ while V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ if V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ if V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) {
+ println!("{}", ipaddr);
+ }
+
+ V4(Ipv4Addr::LOCALHOST).is_ipv4();
+
+ V4(Ipv4Addr::LOCALHOST).is_ipv6();
+
+ V6(Ipv6Addr::LOCALHOST).is_ipv6();
+
+ V6(Ipv6Addr::LOCALHOST).is_ipv4();
+
+ let _ = if V4(Ipv4Addr::LOCALHOST).is_ipv4() {
+ true
+ } else {
+ false
+ };
+
+ ipaddr_const();
+
+ let _ = if gen_ipaddr().is_ipv4() {
+ 1
+ } else if gen_ipaddr().is_ipv6() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_ipaddr() -> IpAddr {
+ V4(Ipv4Addr::LOCALHOST)
+}
+
+const fn ipaddr_const() {
+ if V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ if V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ while V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ while V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ V4(Ipv4Addr::LOCALHOST).is_ipv4();
+
+ V6(Ipv6Addr::LOCALHOST).is_ipv6();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs
new file mode 100644
index 000000000..678d91ce9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs
@@ -0,0 +1,91 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+
+use std::net::{
+ IpAddr::{self, V4, V6},
+ Ipv4Addr, Ipv6Addr,
+};
+
+fn main() {
+ let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST);
+ if let V4(_) = &ipaddr {}
+
+ if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+
+ if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+
+ while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+
+ while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+
+ if V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+
+ if V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+
+ if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) {
+ println!("{}", ipaddr);
+ }
+
+ match V4(Ipv4Addr::LOCALHOST) {
+ V4(_) => true,
+ V6(_) => false,
+ };
+
+ match V4(Ipv4Addr::LOCALHOST) {
+ V4(_) => false,
+ V6(_) => true,
+ };
+
+ match V6(Ipv6Addr::LOCALHOST) {
+ V4(_) => false,
+ V6(_) => true,
+ };
+
+ match V6(Ipv6Addr::LOCALHOST) {
+ V4(_) => true,
+ V6(_) => false,
+ };
+
+ let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) {
+ true
+ } else {
+ false
+ };
+
+ ipaddr_const();
+
+ let _ = if let V4(_) = gen_ipaddr() {
+ 1
+ } else if let V6(_) = gen_ipaddr() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_ipaddr() -> IpAddr {
+ V4(Ipv4Addr::LOCALHOST)
+}
+
+const fn ipaddr_const() {
+ if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+
+ if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+
+ while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+
+ while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+
+ match V4(Ipv4Addr::LOCALHOST) {
+ V4(_) => true,
+ V6(_) => false,
+ };
+
+ match V6(Ipv6Addr::LOCALHOST) {
+ V4(_) => false,
+ V6(_) => true,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr
new file mode 100644
index 000000000..caf458cd8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr
@@ -0,0 +1,130 @@
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12
+ |
+LL | if let V4(_) = &ipaddr {}
+ | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12
+ |
+LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+ | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12
+ |
+LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+ | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15
+ |
+LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+ | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15
+ |
+LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+ | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5
+ |
+LL | / match V4(Ipv4Addr::LOCALHOST) {
+LL | | V4(_) => true,
+LL | | V6(_) => false,
+LL | | };
+ | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5
+ |
+LL | / match V4(Ipv4Addr::LOCALHOST) {
+LL | | V4(_) => false,
+LL | | V6(_) => true,
+LL | | };
+ | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5
+ |
+LL | / match V6(Ipv6Addr::LOCALHOST) {
+LL | | V4(_) => false,
+LL | | V6(_) => true,
+LL | | };
+ | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5
+ |
+LL | / match V6(Ipv6Addr::LOCALHOST) {
+LL | | V4(_) => true,
+LL | | V6(_) => false,
+LL | | };
+ | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20
+ |
+LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) {
+ | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20
+ |
+LL | let _ = if let V4(_) = gen_ipaddr() {
+ | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19
+ |
+LL | } else if let V6(_) = gen_ipaddr() {
+ | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12
+ |
+LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+ | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12
+ |
+LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+ | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15
+ |
+LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {}
+ | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15
+ |
+LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {}
+ | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: redundant pattern matching, consider using `is_ipv4()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5
+ |
+LL | / match V4(Ipv4Addr::LOCALHOST) {
+LL | | V4(_) => true,
+LL | | V6(_) => false,
+LL | | };
+ | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()`
+
+error: redundant pattern matching, consider using `is_ipv6()`
+ --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5
+ |
+LL | / match V6(Ipv6Addr::LOCALHOST) {
+LL | | V4(_) => false,
+LL | | V6(_) => true,
+LL | | };
+ | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
new file mode 100644
index 000000000..a89845c1d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
@@ -0,0 +1,88 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ if None::<()>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ if Some(42).is_some() {
+ foo();
+ } else {
+ bar();
+ }
+
+ while Some(42).is_some() {}
+
+ while Some(42).is_none() {}
+
+ while None::<()>.is_none() {}
+
+ let mut v = vec![1, 2, 3];
+ while v.pop().is_some() {
+ foo();
+ }
+
+ if None::<i32>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ Some(42).is_some();
+
+ None::<()>.is_none();
+
+ let _ = None::<()>.is_none();
+
+ let opt = Some(false);
+ let _ = if opt.is_some() { true } else { false };
+
+ issue6067();
+
+ let _ = if gen_opt().is_some() {
+ 1
+ } else if gen_opt().is_none() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_opt() -> Option<()> {
+ None
+}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if Some(42).is_some() {}
+
+ if None::<()>.is_none() {}
+
+ while Some(42).is_some() {}
+
+ while None::<()>.is_none() {}
+
+ Some(42).is_some();
+
+ None::<()>.is_none();
+}
+
+#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
+fn issue7921() {
+ if (&None::<()>).is_none() {}
+ if (&None::<()>).is_none() {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
new file mode 100644
index 000000000..d6f444034
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
@@ -0,0 +1,103 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ if let None = None::<()> {}
+
+ if let Some(_) = Some(42) {}
+
+ if let Some(_) = Some(42) {
+ foo();
+ } else {
+ bar();
+ }
+
+ while let Some(_) = Some(42) {}
+
+ while let None = Some(42) {}
+
+ while let None = None::<()> {}
+
+ let mut v = vec![1, 2, 3];
+ while let Some(_) = v.pop() {
+ foo();
+ }
+
+ if None::<i32>.is_none() {}
+
+ if Some(42).is_some() {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+
+ let _ = match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+
+ let opt = Some(false);
+ let _ = if let Some(_) = opt { true } else { false };
+
+ issue6067();
+
+ let _ = if let Some(_) = gen_opt() {
+ 1
+ } else if let None = gen_opt() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_opt() -> Option<()> {
+ None
+}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if let Some(_) = Some(42) {}
+
+ if let None = None::<()> {}
+
+ while let Some(_) = Some(42) {}
+
+ while let None = None::<()> {}
+
+ match Some(42) {
+ Some(_) => true,
+ None => false,
+ };
+
+ match None::<()> {
+ Some(_) => false,
+ None => true,
+ };
+}
+
+#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
+fn issue7921() {
+ if let None = *(&None::<()>) {}
+ if let None = *&None::<()> {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
new file mode 100644
index 000000000..27ff812ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
@@ -0,0 +1,146 @@
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:14:12
+ |
+LL | if let None = None::<()> {}
+ | -------^^^^------------- help: try this: `if None::<()>.is_none()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:16:12
+ |
+LL | if let Some(_) = Some(42) {}
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:18:12
+ |
+LL | if let Some(_) = Some(42) {
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:24:15
+ |
+LL | while let Some(_) = Some(42) {}
+ | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:26:15
+ |
+LL | while let None = Some(42) {}
+ | ----------^^^^----------- help: try this: `while Some(42).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:28:15
+ |
+LL | while let None = None::<()> {}
+ | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:31:15
+ |
+LL | while let Some(_) = v.pop() {
+ | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:39:5
+ |
+LL | / match Some(42) {
+LL | | Some(_) => true,
+LL | | None => false,
+LL | | };
+ | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:44:5
+ |
+LL | / match None::<()> {
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:49:13
+ |
+LL | let _ = match None::<()> {
+ | _____________^
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:55:20
+ |
+LL | let _ = if let Some(_) = opt { true } else { false };
+ | -------^^^^^^^------ help: try this: `if opt.is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:59:20
+ |
+LL | let _ = if let Some(_) = gen_opt() {
+ | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:61:19
+ |
+LL | } else if let None = gen_opt() {
+ | -------^^^^------------ help: try this: `if gen_opt().is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:80:12
+ |
+LL | if let Some(_) = Some(42) {}
+ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:82:12
+ |
+LL | if let None = None::<()> {}
+ | -------^^^^------------- help: try this: `if None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:84:15
+ |
+LL | while let Some(_) = Some(42) {}
+ | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:86:15
+ |
+LL | while let None = None::<()> {}
+ | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_option.rs:88:5
+ |
+LL | / match Some(42) {
+LL | | Some(_) => true,
+LL | | None => false,
+LL | | };
+ | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:93:5
+ |
+LL | / match None::<()> {
+LL | | Some(_) => false,
+LL | | None => true,
+LL | | };
+ | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:101:12
+ |
+LL | if let None = *(&None::<()>) {}
+ | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:102:12
+ |
+LL | if let None = *&None::<()> {}
+ | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
new file mode 100644
index 000000000..3645f2c4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
@@ -0,0 +1,76 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+use std::task::Poll::{self, Pending, Ready};
+
+fn main() {
+ if Pending::<()>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ if Ready(42).is_ready() {
+ foo();
+ } else {
+ bar();
+ }
+
+ while Ready(42).is_ready() {}
+
+ while Ready(42).is_pending() {}
+
+ while Pending::<()>.is_pending() {}
+
+ if Pending::<i32>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ Ready(42).is_ready();
+
+ Pending::<()>.is_pending();
+
+ let _ = Pending::<()>.is_pending();
+
+ let poll = Ready(false);
+ let _ = if poll.is_ready() { true } else { false };
+
+ poll_const();
+
+ let _ = if gen_poll().is_ready() {
+ 1
+ } else if gen_poll().is_pending() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_poll() -> Poll<()> {
+ Pending
+}
+
+fn foo() {}
+
+fn bar() {}
+
+const fn poll_const() {
+ if Ready(42).is_ready() {}
+
+ if Pending::<()>.is_pending() {}
+
+ while Ready(42).is_ready() {}
+
+ while Pending::<()>.is_pending() {}
+
+ Ready(42).is_ready();
+
+ Pending::<()>.is_pending();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
new file mode 100644
index 000000000..866c71b7c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
@@ -0,0 +1,91 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::equatable_if_let,
+ clippy::if_same_then_else
+)]
+
+use std::task::Poll::{self, Pending, Ready};
+
+fn main() {
+ if let Pending = Pending::<()> {}
+
+ if let Ready(_) = Ready(42) {}
+
+ if let Ready(_) = Ready(42) {
+ foo();
+ } else {
+ bar();
+ }
+
+ while let Ready(_) = Ready(42) {}
+
+ while let Pending = Ready(42) {}
+
+ while let Pending = Pending::<()> {}
+
+ if Pending::<i32>.is_pending() {}
+
+ if Ready(42).is_ready() {}
+
+ match Ready(42) {
+ Ready(_) => true,
+ Pending => false,
+ };
+
+ match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+
+ let _ = match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+
+ let poll = Ready(false);
+ let _ = if let Ready(_) = poll { true } else { false };
+
+ poll_const();
+
+ let _ = if let Ready(_) = gen_poll() {
+ 1
+ } else if let Pending = gen_poll() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_poll() -> Poll<()> {
+ Pending
+}
+
+fn foo() {}
+
+fn bar() {}
+
+const fn poll_const() {
+ if let Ready(_) = Ready(42) {}
+
+ if let Pending = Pending::<()> {}
+
+ while let Ready(_) = Ready(42) {}
+
+ while let Pending = Pending::<()> {}
+
+ match Ready(42) {
+ Ready(_) => true,
+ Pending => false,
+ };
+
+ match Pending::<()> {
+ Ready(_) => false,
+ Pending => true,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
new file mode 100644
index 000000000..1b480f315
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
@@ -0,0 +1,128 @@
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:16:12
+ |
+LL | if let Pending = Pending::<()> {}
+ | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:18:12
+ |
+LL | if let Ready(_) = Ready(42) {}
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:20:12
+ |
+LL | if let Ready(_) = Ready(42) {
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:26:15
+ |
+LL | while let Ready(_) = Ready(42) {}
+ | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:28:15
+ |
+LL | while let Pending = Ready(42) {}
+ | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:30:15
+ |
+LL | while let Pending = Pending::<()> {}
+ | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:36:5
+ |
+LL | / match Ready(42) {
+LL | | Ready(_) => true,
+LL | | Pending => false,
+LL | | };
+ | |_____^ help: try this: `Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:41:5
+ |
+LL | / match Pending::<()> {
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:46:13
+ |
+LL | let _ = match Pending::<()> {
+ | _____________^
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:52:20
+ |
+LL | let _ = if let Ready(_) = poll { true } else { false };
+ | -------^^^^^^^^------- help: try this: `if poll.is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:56:20
+ |
+LL | let _ = if let Ready(_) = gen_poll() {
+ | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:58:19
+ |
+LL | } else if let Pending = gen_poll() {
+ | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:74:12
+ |
+LL | if let Ready(_) = Ready(42) {}
+ | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:76:12
+ |
+LL | if let Pending = Pending::<()> {}
+ | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:78:15
+ |
+LL | while let Ready(_) = Ready(42) {}
+ | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:80:15
+ |
+LL | while let Pending = Pending::<()> {}
+ | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+ --> $DIR/redundant_pattern_matching_poll.rs:82:5
+ |
+LL | / match Ready(42) {
+LL | | Ready(_) => true,
+LL | | Pending => false,
+LL | | };
+ | |_____^ help: try this: `Ready(42).is_ready()`
+
+error: redundant pattern matching, consider using `is_pending()`
+ --> $DIR/redundant_pattern_matching_poll.rs:87:5
+ |
+LL | / match Pending::<()> {
+LL | | Ready(_) => false,
+LL | | Pending => true,
+LL | | };
+ | |_____^ help: try this: `Pending::<()>.is_pending()`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
new file mode 100644
index 000000000..83c783385
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
@@ -0,0 +1,110 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::unnecessary_wraps,
+ deprecated,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ let result: Result<usize, usize> = Err(5);
+ if result.is_ok() {}
+
+ if Ok::<i32, i32>(42).is_ok() {}
+
+ if Err::<i32, i32>(42).is_err() {}
+
+ while Ok::<i32, i32>(10).is_ok() {}
+
+ while Ok::<i32, i32>(10).is_err() {}
+
+ if Ok::<i32, i32>(42).is_ok() {}
+
+ if Err::<i32, i32>(42).is_err() {}
+
+ if let Ok(x) = Ok::<i32, i32>(42) {
+ println!("{}", x);
+ }
+
+ Ok::<i32, i32>(42).is_ok();
+
+ Ok::<i32, i32>(42).is_err();
+
+ Err::<i32, i32>(42).is_err();
+
+ Err::<i32, i32>(42).is_ok();
+
+ let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false };
+
+ issue5504();
+ issue6067();
+ issue6065();
+
+ let _ = if gen_res().is_ok() {
+ 1
+ } else if gen_res().is_err() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_res() -> Result<(), ()> {
+ Ok(())
+}
+
+macro_rules! m {
+ () => {
+ Some(42u32)
+ };
+}
+
+fn issue5504() {
+ fn result_opt() -> Result<Option<i32>, i32> {
+ Err(42)
+ }
+
+ fn try_result_opt() -> Result<i32, i32> {
+ while (r#try!(result_opt())).is_some() {}
+ if (r#try!(result_opt())).is_some() {}
+ Ok(42)
+ }
+
+ try_result_opt();
+
+ if m!().is_some() {}
+ while m!().is_some() {}
+}
+
+fn issue6065() {
+ macro_rules! if_let_in_macro {
+ ($pat:pat, $x:expr) => {
+ if let Some($pat) = $x {}
+ };
+ }
+
+ // shouldn't be linted
+ if_let_in_macro!(_, Some(42));
+}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if Ok::<i32, i32>(42).is_ok() {}
+
+ if Err::<i32, i32>(42).is_err() {}
+
+ while Ok::<i32, i32>(10).is_ok() {}
+
+ while Ok::<i32, i32>(10).is_err() {}
+
+ Ok::<i32, i32>(42).is_ok();
+
+ Err::<i32, i32>(42).is_err();
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
new file mode 100644
index 000000000..e06d4485a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
@@ -0,0 +1,128 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+ unused_must_use,
+ clippy::needless_bool,
+ clippy::match_like_matches_macro,
+ clippy::unnecessary_wraps,
+ deprecated,
+ clippy::if_same_then_else
+)]
+
+fn main() {
+ let result: Result<usize, usize> = Err(5);
+ if let Ok(_) = &result {}
+
+ if let Ok(_) = Ok::<i32, i32>(42) {}
+
+ if let Err(_) = Err::<i32, i32>(42) {}
+
+ while let Ok(_) = Ok::<i32, i32>(10) {}
+
+ while let Err(_) = Ok::<i32, i32>(10) {}
+
+ if Ok::<i32, i32>(42).is_ok() {}
+
+ if Err::<i32, i32>(42).is_err() {}
+
+ if let Ok(x) = Ok::<i32, i32>(42) {
+ println!("{}", x);
+ }
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
+
+ issue5504();
+ issue6067();
+ issue6065();
+
+ let _ = if let Ok(_) = gen_res() {
+ 1
+ } else if let Err(_) = gen_res() {
+ 2
+ } else {
+ 3
+ };
+}
+
+fn gen_res() -> Result<(), ()> {
+ Ok(())
+}
+
+macro_rules! m {
+ () => {
+ Some(42u32)
+ };
+}
+
+fn issue5504() {
+ fn result_opt() -> Result<Option<i32>, i32> {
+ Err(42)
+ }
+
+ fn try_result_opt() -> Result<i32, i32> {
+ while let Some(_) = r#try!(result_opt()) {}
+ if let Some(_) = r#try!(result_opt()) {}
+ Ok(42)
+ }
+
+ try_result_opt();
+
+ if let Some(_) = m!() {}
+ while let Some(_) = m!() {}
+}
+
+fn issue6065() {
+ macro_rules! if_let_in_macro {
+ ($pat:pat, $x:expr) => {
+ if let Some($pat) = $x {}
+ };
+ }
+
+ // shouldn't be linted
+ if_let_in_macro!(_, Some(42));
+}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+ if let Ok(_) = Ok::<i32, i32>(42) {}
+
+ if let Err(_) = Err::<i32, i32>(42) {}
+
+ while let Ok(_) = Ok::<i32, i32>(10) {}
+
+ while let Err(_) = Ok::<i32, i32>(10) {}
+
+ match Ok::<i32, i32>(42) {
+ Ok(_) => true,
+ Err(_) => false,
+ };
+
+ match Err::<i32, i32>(42) {
+ Ok(_) => false,
+ Err(_) => true,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
new file mode 100644
index 000000000..d674d061e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
@@ -0,0 +1,154 @@
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:16:12
+ |
+LL | if let Ok(_) = &result {}
+ | -------^^^^^---------- help: try this: `if result.is_ok()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:18:12
+ |
+LL | if let Ok(_) = Ok::<i32, i32>(42) {}
+ | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:20:12
+ |
+LL | if let Err(_) = Err::<i32, i32>(42) {}
+ | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:22:15
+ |
+LL | while let Ok(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:24:15
+ |
+LL | while let Err(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:34:5
+ |
+LL | / match Ok::<i32, i32>(42) {
+LL | | Ok(_) => true,
+LL | | Err(_) => false,
+LL | | };
+ | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:39:5
+ |
+LL | / match Ok::<i32, i32>(42) {
+LL | | Ok(_) => false,
+LL | | Err(_) => true,
+LL | | };
+ | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:44:5
+ |
+LL | / match Err::<i32, i32>(42) {
+LL | | Ok(_) => false,
+LL | | Err(_) => true,
+LL | | };
+ | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:49:5
+ |
+LL | / match Err::<i32, i32>(42) {
+LL | | Ok(_) => true,
+LL | | Err(_) => false,
+LL | | };
+ | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:54:20
+ |
+LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
+ | -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:60:20
+ |
+LL | let _ = if let Ok(_) = gen_res() {
+ | -------^^^^^------------ help: try this: `if gen_res().is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:62:19
+ |
+LL | } else if let Err(_) = gen_res() {
+ | -------^^^^^^------------ help: try this: `if gen_res().is_err()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_result.rs:85:19
+ |
+LL | while let Some(_) = r#try!(result_opt()) {}
+ | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_result.rs:86:16
+ |
+LL | if let Some(_) = r#try!(result_opt()) {}
+ | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_result.rs:92:12
+ |
+LL | if let Some(_) = m!() {}
+ | -------^^^^^^^------- help: try this: `if m!().is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/redundant_pattern_matching_result.rs:93:15
+ |
+LL | while let Some(_) = m!() {}
+ | ----------^^^^^^^------- help: try this: `while m!().is_some()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:111:12
+ |
+LL | if let Ok(_) = Ok::<i32, i32>(42) {}
+ | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:113:12
+ |
+LL | if let Err(_) = Err::<i32, i32>(42) {}
+ | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:115:15
+ |
+LL | while let Ok(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:117:15
+ |
+LL | while let Err(_) = Ok::<i32, i32>(10) {}
+ | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+ --> $DIR/redundant_pattern_matching_result.rs:119:5
+ |
+LL | / match Ok::<i32, i32>(42) {
+LL | | Ok(_) => true,
+LL | | Err(_) => false,
+LL | | };
+ | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+ --> $DIR/redundant_pattern_matching_result.rs:124:5
+ |
+LL | / match Err::<i32, i32>(42) {
+LL | | Ok(_) => false,
+LL | | Err(_) => true,
+LL | | };
+ | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed
new file mode 100644
index 000000000..106947de6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed
@@ -0,0 +1,117 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::redundant_pub_crate)]
+
+mod m1 {
+ fn f() {}
+ pub fn g() {} // private due to m1
+ pub fn h() {}
+
+ mod m1_1 {
+ fn f() {}
+ pub fn g() {} // private due to m1_1 and m1
+ pub fn h() {}
+ }
+
+ pub mod m1_2 {
+ // ^ private due to m1
+ fn f() {}
+ pub fn g() {} // private due to m1_2 and m1
+ pub fn h() {}
+ }
+
+ pub mod m1_3 {
+ fn f() {}
+ pub fn g() {} // private due to m1
+ pub fn h() {}
+ }
+}
+
+pub(crate) mod m2 {
+ fn f() {}
+ pub fn g() {} // already crate visible due to m2
+ pub fn h() {}
+
+ mod m2_1 {
+ fn f() {}
+ pub fn g() {} // private due to m2_1
+ pub fn h() {}
+ }
+
+ pub mod m2_2 {
+ // ^ already crate visible due to m2
+ fn f() {}
+ pub fn g() {} // already crate visible due to m2_2 and m2
+ pub fn h() {}
+ }
+
+ pub mod m2_3 {
+ fn f() {}
+ pub fn g() {} // already crate visible due to m2
+ pub fn h() {}
+ }
+}
+
+pub mod m3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m3 is exported
+ pub fn h() {}
+
+ mod m3_1 {
+ fn f() {}
+ pub fn g() {} // private due to m3_1
+ pub fn h() {}
+ }
+
+ pub(crate) mod m3_2 {
+ // ^ ok
+ fn f() {}
+ pub fn g() {} // already crate visible due to m3_2
+ pub fn h() {}
+ }
+
+ pub mod m3_3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m3 and m3_3 are exported
+ pub fn h() {}
+ }
+}
+
+mod m4 {
+ fn f() {}
+ pub fn g() {} // private: not re-exported by `pub use m4::*`
+ pub fn h() {}
+
+ mod m4_1 {
+ fn f() {}
+ pub fn g() {} // private due to m4_1
+ pub fn h() {}
+ }
+
+ pub mod m4_2 {
+ // ^ private: not re-exported by `pub use m4::*`
+ fn f() {}
+ pub fn g() {} // private due to m4_2
+ pub fn h() {}
+ }
+
+ pub mod m4_3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
+ pub fn h() {}
+ }
+}
+
+pub use m4::*;
+
+mod issue_8732 {
+ #[allow(unused_macros)]
+ macro_rules! some_macro {
+ () => {};
+ }
+
+ #[allow(unused_imports)]
+ pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs
new file mode 100644
index 000000000..f96cfd318
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs
@@ -0,0 +1,117 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::redundant_pub_crate)]
+
+mod m1 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m1
+ pub fn h() {}
+
+ mod m1_1 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m1_1 and m1
+ pub fn h() {}
+ }
+
+ pub(crate) mod m1_2 {
+ // ^ private due to m1
+ fn f() {}
+ pub(crate) fn g() {} // private due to m1_2 and m1
+ pub fn h() {}
+ }
+
+ pub mod m1_3 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m1
+ pub fn h() {}
+ }
+}
+
+pub(crate) mod m2 {
+ fn f() {}
+ pub(crate) fn g() {} // already crate visible due to m2
+ pub fn h() {}
+
+ mod m2_1 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m2_1
+ pub fn h() {}
+ }
+
+ pub(crate) mod m2_2 {
+ // ^ already crate visible due to m2
+ fn f() {}
+ pub(crate) fn g() {} // already crate visible due to m2_2 and m2
+ pub fn h() {}
+ }
+
+ pub mod m2_3 {
+ fn f() {}
+ pub(crate) fn g() {} // already crate visible due to m2
+ pub fn h() {}
+ }
+}
+
+pub mod m3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m3 is exported
+ pub fn h() {}
+
+ mod m3_1 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m3_1
+ pub fn h() {}
+ }
+
+ pub(crate) mod m3_2 {
+ // ^ ok
+ fn f() {}
+ pub(crate) fn g() {} // already crate visible due to m3_2
+ pub fn h() {}
+ }
+
+ pub mod m3_3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m3 and m3_3 are exported
+ pub fn h() {}
+ }
+}
+
+mod m4 {
+ fn f() {}
+ pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
+ pub fn h() {}
+
+ mod m4_1 {
+ fn f() {}
+ pub(crate) fn g() {} // private due to m4_1
+ pub fn h() {}
+ }
+
+ pub(crate) mod m4_2 {
+ // ^ private: not re-exported by `pub use m4::*`
+ fn f() {}
+ pub(crate) fn g() {} // private due to m4_2
+ pub fn h() {}
+ }
+
+ pub mod m4_3 {
+ fn f() {}
+ pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
+ pub fn h() {}
+ }
+}
+
+pub use m4::*;
+
+mod issue_8732 {
+ #[allow(unused_macros)]
+ macro_rules! some_macro {
+ () => {};
+ }
+
+ #[allow(unused_imports)]
+ pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr
new file mode 100644
index 000000000..6fccdaa4e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr
@@ -0,0 +1,132 @@
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:7:5
+ |
+LL | pub(crate) fn g() {} // private due to m1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+ |
+ = note: `-D clippy::redundant-pub-crate` implied by `-D warnings`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:12:9
+ |
+LL | pub(crate) fn g() {} // private due to m1_1 and m1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) module inside private module
+ --> $DIR/redundant_pub_crate.rs:16:5
+ |
+LL | pub(crate) mod m1_2 {
+ | ----------^^^^^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:19:9
+ |
+LL | pub(crate) fn g() {} // private due to m1_2 and m1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:25:9
+ |
+LL | pub(crate) fn g() {} // private due to m1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:32:5
+ |
+LL | pub(crate) fn g() {} // already crate visible due to m2
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:37:9
+ |
+LL | pub(crate) fn g() {} // private due to m2_1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) module inside private module
+ --> $DIR/redundant_pub_crate.rs:41:5
+ |
+LL | pub(crate) mod m2_2 {
+ | ----------^^^^^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:44:9
+ |
+LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:50:9
+ |
+LL | pub(crate) fn g() {} // already crate visible due to m2
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:62:9
+ |
+LL | pub(crate) fn g() {} // private due to m3_1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:69:9
+ |
+LL | pub(crate) fn g() {} // already crate visible due to m3_2
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:82:5
+ |
+LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:87:9
+ |
+LL | pub(crate) fn g() {} // private due to m4_1
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) module inside private module
+ --> $DIR/redundant_pub_crate.rs:91:5
+ |
+LL | pub(crate) mod m4_2 {
+ | ----------^^^^^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: pub(crate) function inside private module
+ --> $DIR/redundant_pub_crate.rs:94:9
+ |
+LL | pub(crate) fn g() {} // private due to m4_2
+ | ----------^^^^^
+ | |
+ | help: consider using: `pub`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_slicing.fixed b/src/tools/clippy/tests/ui/redundant_slicing.fixed
new file mode 100644
index 000000000..8dd8d3092
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_slicing.fixed
@@ -0,0 +1,46 @@
+// run-rustfix
+
+#![allow(unused, clippy::deref_by_slicing)]
+#![warn(clippy::redundant_slicing)]
+
+use std::io::Read;
+
+fn main() {
+ let slice: &[u32] = &[0];
+ let _ = slice; // Redundant slice
+
+ let v = vec![0];
+ let _ = &v[..]; // Ok, results in `&[_]`
+ let _ = (&*v); // Outer borrow is redundant
+
+ static S: &[u8] = &[0, 1, 2];
+ let _ = &mut &S[..]; // Ok, re-borrows slice
+
+ let mut vec = vec![0];
+ let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
+ let _ = &mut mut_slice[..]; // Ok, re-borrows slice
+
+ let ref_vec = &vec;
+ let _ = &ref_vec[..]; // Ok, results in `&[_]`
+
+ macro_rules! m {
+ ($e:expr) => {
+ $e
+ };
+ }
+ let _ = slice;
+
+ macro_rules! m2 {
+ ($e:expr) => {
+ &$e[..]
+ };
+ }
+ let _ = m2!(slice); // Don't lint in a macro
+
+ let slice_ref = &slice;
+ let _ = &slice_ref[..]; // Ok, derefs slice
+
+ // Issue #7972
+ let bytes: &[u8] = &[];
+ let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
+}
diff --git a/src/tools/clippy/tests/ui/redundant_slicing.rs b/src/tools/clippy/tests/ui/redundant_slicing.rs
new file mode 100644
index 000000000..51c16dd8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_slicing.rs
@@ -0,0 +1,46 @@
+// run-rustfix
+
+#![allow(unused, clippy::deref_by_slicing)]
+#![warn(clippy::redundant_slicing)]
+
+use std::io::Read;
+
+fn main() {
+ let slice: &[u32] = &[0];
+ let _ = &slice[..]; // Redundant slice
+
+ let v = vec![0];
+ let _ = &v[..]; // Ok, results in `&[_]`
+ let _ = &(&*v)[..]; // Outer borrow is redundant
+
+ static S: &[u8] = &[0, 1, 2];
+ let _ = &mut &S[..]; // Ok, re-borrows slice
+
+ let mut vec = vec![0];
+ let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
+ let _ = &mut mut_slice[..]; // Ok, re-borrows slice
+
+ let ref_vec = &vec;
+ let _ = &ref_vec[..]; // Ok, results in `&[_]`
+
+ macro_rules! m {
+ ($e:expr) => {
+ $e
+ };
+ }
+ let _ = &m!(slice)[..];
+
+ macro_rules! m2 {
+ ($e:expr) => {
+ &$e[..]
+ };
+ }
+ let _ = m2!(slice); // Don't lint in a macro
+
+ let slice_ref = &slice;
+ let _ = &slice_ref[..]; // Ok, derefs slice
+
+ // Issue #7972
+ let bytes: &[u8] = &[];
+ let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
+}
diff --git a/src/tools/clippy/tests/ui/redundant_slicing.stderr b/src/tools/clippy/tests/ui/redundant_slicing.stderr
new file mode 100644
index 000000000..82367143c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_slicing.stderr
@@ -0,0 +1,22 @@
+error: redundant slicing of the whole range
+ --> $DIR/redundant_slicing.rs:10:13
+ |
+LL | let _ = &slice[..]; // Redundant slice
+ | ^^^^^^^^^^ help: use the original value instead: `slice`
+ |
+ = note: `-D clippy::redundant-slicing` implied by `-D warnings`
+
+error: redundant slicing of the whole range
+ --> $DIR/redundant_slicing.rs:14:13
+ |
+LL | let _ = &(&*v)[..]; // Outer borrow is redundant
+ | ^^^^^^^^^^ help: use the original value instead: `(&*v)`
+
+error: redundant slicing of the whole range
+ --> $DIR/redundant_slicing.rs:31:13
+ |
+LL | let _ = &m!(slice)[..];
+ | ^^^^^^^^^^^^^^ help: use the original value instead: `slice`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
new file mode 100644
index 000000000..acc8f1e25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
@@ -0,0 +1,56 @@
+// run-rustfix
+
+#![allow(unused)]
+
+#[derive(Debug)]
+struct Foo;
+
+const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
+
+const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
+
+const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
+
+const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+
+const VAR_SIX: &u8 = &5;
+
+const VAR_HEIGHT: &Foo = &Foo {};
+
+const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+
+const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+
+const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
+
+static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
+
+static STATIC_VAR_SIX: &u8 = &5;
+
+static STATIC_VAR_HEIGHT: &Foo = &Foo {};
+
+static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+
+static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
+
+fn main() {
+ let false_positive: &'static str = "test";
+}
+
+trait Bar {
+ const TRAIT_VAR: &'static str;
+}
+
+impl Foo {
+ const IMPL_VAR: &'static str = "var";
+}
+
+impl Bar for Foo {
+ const TRAIT_VAR: &'static str = "foo";
+}
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
new file mode 100644
index 000000000..f2f0f7865
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
@@ -0,0 +1,56 @@
+// run-rustfix
+
+#![allow(unused)]
+
+#[derive(Debug)]
+struct Foo;
+
+const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
+
+const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
+
+const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+
+const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+
+const VAR_SIX: &'static u8 = &5;
+
+const VAR_HEIGHT: &'static Foo = &Foo {};
+
+const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+
+const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+
+const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
+
+static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+
+static STATIC_VAR_SIX: &'static u8 = &5;
+
+static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
+
+static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
+
+static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+
+static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+
+fn main() {
+ let false_positive: &'static str = "test";
+}
+
+trait Bar {
+ const TRAIT_VAR: &'static str;
+}
+
+impl Foo {
+ const IMPL_VAR: &'static str = "var";
+}
+
+impl Bar for Foo {
+ const TRAIT_VAR: &'static str = "foo";
+}
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
new file mode 100644
index 000000000..649831f9c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
@@ -0,0 +1,100 @@
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:8:17
+ |
+LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+ |
+ = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:12:21
+ |
+LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:14:32
+ |
+LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:14:47
+ |
+LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:16:17
+ |
+LL | const VAR_SIX: &'static u8 = &5;
+ | -^^^^^^^--- help: consider removing `'static`: `&u8`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:18:20
+ |
+LL | const VAR_HEIGHT: &'static Foo = &Foo {};
+ | -^^^^^^^---- help: consider removing `'static`: `&Foo`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:20:19
+ |
+LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
+ | -^^^^^^^----- help: consider removing `'static`: `&[u8]`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:22:19
+ |
+LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+ | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:24:19
+ |
+LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+ | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:26:25
+ |
+LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:30:29
+ |
+LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:32:25
+ |
+LL | static STATIC_VAR_SIX: &'static u8 = &5;
+ | -^^^^^^^--- help: consider removing `'static`: `&u8`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:34:28
+ |
+LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
+ | -^^^^^^^---- help: consider removing `'static`: `&Foo`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:36:27
+ |
+LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
+ | -^^^^^^^----- help: consider removing `'static`: `&[u8]`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:38:27
+ |
+LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
+ | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:40:27
+ |
+LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+ | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
new file mode 100644
index 000000000..f57dd58e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
@@ -0,0 +1,13 @@
+// these are rustfixable, but run-rustfix tests cannot handle them
+
+const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+
+const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+
+static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+
+static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+
+static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
new file mode 100644
index 000000000..cc7e55a75
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
@@ -0,0 +1,64 @@
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:3:18
+ |
+LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+ | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
+ |
+ = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:3:30
+ |
+LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:5:29
+ |
+LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+ | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
+
+error: constants have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:5:39
+ |
+LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:7:40
+ |
+LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:7:55
+ |
+LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:9:26
+ |
+LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+ | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:9:38
+ |
+LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:11:37
+ |
+LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+ | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes_multiple.rs:11:47
+ |
+LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
+ | -^^^^^^^---- help: consider removing `'static`: `&str`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ref_binding_to_reference.rs b/src/tools/clippy/tests/ui/ref_binding_to_reference.rs
new file mode 100644
index 000000000..c8d0e56b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_binding_to_reference.rs
@@ -0,0 +1,85 @@
+// FIXME: run-rustfix waiting on multi-span suggestions
+
+#![feature(lint_reasons)]
+#![warn(clippy::ref_binding_to_reference)]
+#![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)]
+
+fn f1(_: &str) {}
+macro_rules! m2 {
+ ($e:expr) => {
+ f1(*$e)
+ };
+}
+macro_rules! m3 {
+ ($i:ident) => {
+ Some(ref $i)
+ };
+}
+
+#[allow(dead_code)]
+fn main() {
+ let x = String::new();
+
+ // Ok, the pattern is from a macro
+ let _: &&String = match Some(&x) {
+ m3!(x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &&String = match Some(&x) {
+ Some(ref x) => x,
+ None => return,
+ };
+
+ // Err, reference to a &String
+ let _: &&String = match Some(&x) {
+ Some(ref x) => {
+ f1(x);
+ f1(*x);
+ x
+ },
+ None => return,
+ };
+
+ // Err, reference to a &String
+ match Some(&x) {
+ Some(ref x) => m2!(x),
+ None => return,
+ }
+
+ // Err, reference to a &String
+ let _ = |&ref x: &&String| {
+ let _: &&String = x;
+ };
+}
+
+// Err, reference to a &String
+fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ let _: &&String = x;
+ *x
+}
+
+trait T1 {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &&String = x;
+ }
+}
+
+struct S;
+impl T1 for S {
+ // Err, reference to a &String
+ fn f(&ref x: &&String) {
+ let _: &&String = x;
+ }
+}
+
+fn check_expect_suppression() {
+ let x = String::new();
+ #[expect(clippy::ref_binding_to_reference)]
+ let _: &&String = match Some(&x) {
+ Some(ref x) => x,
+ None => return,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr b/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr
new file mode 100644
index 000000000..eb36cd516
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_binding_to_reference.stderr
@@ -0,0 +1,88 @@
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:31:14
+ |
+LL | Some(ref x) => x,
+ | ^^^^^
+ |
+ = note: `-D clippy::ref-binding-to-reference` implied by `-D warnings`
+help: try this
+ |
+LL | Some(x) => &x,
+ | ~ ~~
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:37:14
+ |
+LL | Some(ref x) => {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ Some(x) => {
+LL | f1(x);
+LL ~ f1(x);
+LL ~ &x
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:47:14
+ |
+LL | Some(ref x) => m2!(x),
+ | ^^^^^
+ |
+help: try this
+ |
+LL | Some(x) => m2!(&x),
+ | ~ ~~
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:52:15
+ |
+LL | let _ = |&ref x: &&String| {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ let _ = |&x: &&String| {
+LL ~ let _: &&String = &x;
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:58:12
+ |
+LL | fn f2<'a>(&ref x: &&'a String) -> &'a String {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f2<'a>(&x: &&'a String) -> &'a String {
+LL ~ let _: &&String = &x;
+LL ~ x
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:65:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &&String = &x;
+ |
+
+error: this pattern creates a reference to a reference
+ --> $DIR/ref_binding_to_reference.rs:73:11
+ |
+LL | fn f(&ref x: &&String) {
+ | ^^^^^
+ |
+help: try this
+ |
+LL ~ fn f(&x: &&String) {
+LL ~ let _: &&String = &x;
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ref_option_ref.rs b/src/tools/clippy/tests/ui/ref_option_ref.rs
new file mode 100644
index 000000000..2df45c927
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_option_ref.rs
@@ -0,0 +1,47 @@
+#![allow(unused)]
+#![warn(clippy::ref_option_ref)]
+
+// This lint is not tagged as run-rustfix because automatically
+// changing the type of a variable would also means changing
+// all usages of this variable to match and This is not handled
+// by this lint.
+
+static THRESHOLD: i32 = 10;
+static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
+const CONST_THRESHOLD: &i32 = &10;
+const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD);
+
+type RefOptRefU32<'a> = &'a Option<&'a u32>;
+type RefOptRef<'a, T> = &'a Option<&'a T>;
+
+fn foo(data: &Option<&u32>) {}
+
+fn bar(data: &u32) -> &Option<&u32> {
+ &None
+}
+
+struct StructRef<'a> {
+ data: &'a Option<&'a u32>,
+}
+
+struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
+
+enum EnumRef<'a> {
+ Variant1(u32),
+ Variant2(&'a Option<&'a u32>),
+}
+
+trait RefOptTrait {
+ type A;
+ fn foo(&self, _: Self::A);
+}
+
+impl RefOptTrait for u32 {
+ type A = &'static Option<&'static Self>;
+
+ fn foo(&self, _: Self::A) {}
+}
+
+fn main() {
+ let x: &Option<&u32> = &None;
+}
diff --git a/src/tools/clippy/tests/ui/ref_option_ref.stderr b/src/tools/clippy/tests/ui/ref_option_ref.stderr
new file mode 100644
index 000000000..b61334758
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_option_ref.stderr
@@ -0,0 +1,70 @@
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:10:23
+ |
+LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
+ | ^^^^^^^^^^^^^ help: try: `Option<&i32>`
+ |
+ = note: `-D clippy::ref-option-ref` implied by `-D warnings`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:12:18
+ |
+LL | const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD);
+ | ^^^^^^^^^^^^^ help: try: `Option<&i32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:14:25
+ |
+LL | type RefOptRefU32<'a> = &'a Option<&'a u32>;
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:15:25
+ |
+LL | type RefOptRef<'a, T> = &'a Option<&'a T>;
+ | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:17:14
+ |
+LL | fn foo(data: &Option<&u32>) {}
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:19:23
+ |
+LL | fn bar(data: &u32) -> &Option<&u32> {
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:24:11
+ |
+LL | data: &'a Option<&'a u32>,
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:27:32
+ |
+LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:31:14
+ |
+LL | Variant2(&'a Option<&'a u32>),
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:40:14
+ |
+LL | type A = &'static Option<&'static Self>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:46:12
+ |
+LL | let x: &Option<&u32> = &None;
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs
new file mode 100644
index 000000000..f7f3b195c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/regex.rs
@@ -0,0 +1,82 @@
+#![allow(unused)]
+#![warn(clippy::invalid_regex, clippy::trivial_regex)]
+
+extern crate regex;
+
+use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet};
+use regex::{Regex, RegexBuilder, RegexSet};
+
+const OPENING_PAREN: &str = "(";
+const NOT_A_REAL_REGEX: &str = "foobar";
+
+fn syntax_error() {
+ let pipe_in_wrong_position = Regex::new("|");
+ let pipe_in_wrong_position_builder = RegexBuilder::new("|");
+ let wrong_char_ranice = Regex::new("[z-a]");
+ let some_unicode = Regex::new("[é-è]");
+
+ let some_regex = Regex::new(OPENING_PAREN);
+
+ let binary_pipe_in_wrong_position = BRegex::new("|");
+ let some_binary_regex = BRegex::new(OPENING_PAREN);
+ let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
+
+ let closing_paren = ")";
+ let not_linted = Regex::new(closing_paren);
+
+ let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
+ let bset = BRegexSet::new(&[
+ r"[a-z]+@[a-z]+\.(com|org|net)",
+ r"[a-z]+\.(com|org|net)",
+ r".", // regression test
+ ]);
+
+ let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
+ let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
+
+ let raw_string_error = Regex::new(r"[...\/...]");
+ let raw_string_error = Regex::new(r#"[...\/...]"#);
+}
+
+fn trivial_regex() {
+ let trivial_eq = Regex::new("^foobar$");
+
+ let trivial_eq_builder = RegexBuilder::new("^foobar$");
+
+ let trivial_starts_with = Regex::new("^foobar");
+
+ let trivial_ends_with = Regex::new("foobar$");
+
+ let trivial_contains = Regex::new("foobar");
+
+ let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
+
+ let trivial_backslash = Regex::new("a\\.b");
+
+ // unlikely corner cases
+ let trivial_empty = Regex::new("");
+
+ let trivial_empty = Regex::new("^");
+
+ let trivial_empty = Regex::new("^$");
+
+ let binary_trivial_empty = BRegex::new("^$");
+
+ // non-trivial regexes
+ let non_trivial_dot = Regex::new("a.b");
+ let non_trivial_dot_builder = RegexBuilder::new("a.b");
+ let non_trivial_eq = Regex::new("^foo|bar$");
+ let non_trivial_starts_with = Regex::new("^foo|bar");
+ let non_trivial_ends_with = Regex::new("^foo|bar");
+ let non_trivial_ends_with = Regex::new("foo|bar");
+ let non_trivial_binary = BRegex::new("foo|bar");
+ let non_trivial_binary_builder = BRegexBuilder::new("foo|bar");
+
+ // #6005: unicode classes in bytes::Regex
+ let a_byte_of_unicode = BRegex::new(r"\p{C}");
+}
+
+fn main() {
+ syntax_error();
+ trivial_regex();
+}
diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr
new file mode 100644
index 000000000..1394a9b63
--- /dev/null
+++ b/src/tools/clippy/tests/ui/regex.stderr
@@ -0,0 +1,171 @@
+error: trivial regex
+ --> $DIR/regex.rs:13:45
+ |
+LL | let pipe_in_wrong_position = Regex::new("|");
+ | ^^^
+ |
+ = note: `-D clippy::trivial-regex` implied by `-D warnings`
+ = help: the regex is unlikely to be useful as it is
+
+error: trivial regex
+ --> $DIR/regex.rs:14:60
+ |
+LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
+ | ^^^
+ |
+ = help: the regex is unlikely to be useful as it is
+
+error: regex syntax error: invalid character class range, the start must be <= the end
+ --> $DIR/regex.rs:15:42
+ |
+LL | let wrong_char_ranice = Regex::new("[z-a]");
+ | ^^^
+ |
+ = note: `-D clippy::invalid-regex` implied by `-D warnings`
+
+error: regex syntax error: invalid character class range, the start must be <= the end
+ --> $DIR/regex.rs:16:37
+ |
+LL | let some_unicode = Regex::new("[é-è]");
+ | ^^^
+
+error: regex syntax error on position 0: unclosed group
+ --> $DIR/regex.rs:18:33
+ |
+LL | let some_regex = Regex::new(OPENING_PAREN);
+ | ^^^^^^^^^^^^^
+
+error: trivial regex
+ --> $DIR/regex.rs:20:53
+ |
+LL | let binary_pipe_in_wrong_position = BRegex::new("|");
+ | ^^^
+ |
+ = help: the regex is unlikely to be useful as it is
+
+error: regex syntax error on position 0: unclosed group
+ --> $DIR/regex.rs:21:41
+ |
+LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
+ | ^^^^^^^^^^^^^
+
+error: regex syntax error on position 0: unclosed group
+ --> $DIR/regex.rs:22:56
+ |
+LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
+ | ^^^^^^^^^^^^^
+
+error: regex syntax error on position 0: unclosed group
+ --> $DIR/regex.rs:34:37
+ |
+LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
+ | ^^^^^^^^^^^^^
+
+error: regex syntax error on position 0: unclosed group
+ --> $DIR/regex.rs:35:39
+ |
+LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
+ | ^^^^^^^^^^^^^
+
+error: regex syntax error: unrecognized escape sequence
+ --> $DIR/regex.rs:37:45
+ |
+LL | let raw_string_error = Regex::new(r"[...//...]");
+ | ^^
+
+error: regex syntax error: unrecognized escape sequence
+ --> $DIR/regex.rs:38:46
+ |
+LL | let raw_string_error = Regex::new(r#"[...//...]"#);
+ | ^^
+
+error: trivial regex
+ --> $DIR/regex.rs:42:33
+ |
+LL | let trivial_eq = Regex::new("^foobar$");
+ | ^^^^^^^^^^
+ |
+ = help: consider using `==` on `str`s
+
+error: trivial regex
+ --> $DIR/regex.rs:44:48
+ |
+LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
+ | ^^^^^^^^^^
+ |
+ = help: consider using `==` on `str`s
+
+error: trivial regex
+ --> $DIR/regex.rs:46:42
+ |
+LL | let trivial_starts_with = Regex::new("^foobar");
+ | ^^^^^^^^^
+ |
+ = help: consider using `str::starts_with`
+
+error: trivial regex
+ --> $DIR/regex.rs:48:40
+ |
+LL | let trivial_ends_with = Regex::new("foobar$");
+ | ^^^^^^^^^
+ |
+ = help: consider using `str::ends_with`
+
+error: trivial regex
+ --> $DIR/regex.rs:50:39
+ |
+LL | let trivial_contains = Regex::new("foobar");
+ | ^^^^^^^^
+ |
+ = help: consider using `str::contains`
+
+error: trivial regex
+ --> $DIR/regex.rs:52:39
+ |
+LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `str::contains`
+
+error: trivial regex
+ --> $DIR/regex.rs:54:40
+ |
+LL | let trivial_backslash = Regex::new("a/.b");
+ | ^^^^^^^
+ |
+ = help: consider using `str::contains`
+
+error: trivial regex
+ --> $DIR/regex.rs:57:36
+ |
+LL | let trivial_empty = Regex::new("");
+ | ^^
+ |
+ = help: the regex is unlikely to be useful as it is
+
+error: trivial regex
+ --> $DIR/regex.rs:59:36
+ |
+LL | let trivial_empty = Regex::new("^");
+ | ^^^
+ |
+ = help: the regex is unlikely to be useful as it is
+
+error: trivial regex
+ --> $DIR/regex.rs:61:36
+ |
+LL | let trivial_empty = Regex::new("^$");
+ | ^^^^
+ |
+ = help: consider using `str::is_empty`
+
+error: trivial regex
+ --> $DIR/regex.rs:63:44
+ |
+LL | let binary_trivial_empty = BRegex::new("^$");
+ | ^^^^
+ |
+ = help: consider using `str::is_empty`
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
new file mode 100644
index 000000000..53288be94
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -0,0 +1,72 @@
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+// run-rustfix
+
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
+#![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::mixed_read_write_in_expression)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::expect_used)]
+#![allow(clippy::map_unwrap_or)]
+#![allow(clippy::unwrap_used)]
+#![allow(clippy::needless_borrow)]
+#![allow(clippy::single_char_add_str)]
+#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::recursive_format_impl)]
+#![allow(clippy::invisible_characters)]
+#![allow(drop_bounds)]
+#![allow(array_into_iter)]
+#![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
+#![allow(enum_intrinsics_non_enums)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::box_collection)]
+#![warn(clippy::redundant_static_lifetimes)]
+#![warn(clippy::cognitive_complexity)]
+#![warn(clippy::disallowed_methods)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::mixed_read_write_in_expression)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::useless_conversion)]
+#![warn(clippy::match_result_ok)]
+#![warn(clippy::new_without_default)]
+#![warn(clippy::bind_instead_of_map)]
+#![warn(clippy::expect_used)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::unwrap_used)]
+#![warn(clippy::needless_borrow)]
+#![warn(clippy::expect_used)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::unwrap_used)]
+#![warn(clippy::single_char_add_str)]
+#![warn(clippy::module_name_repetitions)]
+#![warn(clippy::recursive_format_impl)]
+#![warn(clippy::invisible_characters)]
+#![warn(drop_bounds)]
+#![warn(array_into_iter)]
+#![warn(invalid_atomic_ordering)]
+#![warn(invalid_value)]
+#![warn(enum_intrinsics_non_enums)]
+#![warn(non_fmt_panics)]
+#![warn(temporary_cstring_as_ptr)]
+#![warn(unknown_lints)]
+#![warn(unused_labels)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
new file mode 100644
index 000000000..539f34f84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -0,0 +1,72 @@
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
+// run-rustfix
+
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
+#![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::mixed_read_write_in_expression)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
+#![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::expect_used)]
+#![allow(clippy::map_unwrap_or)]
+#![allow(clippy::unwrap_used)]
+#![allow(clippy::needless_borrow)]
+#![allow(clippy::single_char_add_str)]
+#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::recursive_format_impl)]
+#![allow(clippy::invisible_characters)]
+#![allow(drop_bounds)]
+#![allow(array_into_iter)]
+#![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
+#![allow(enum_intrinsics_non_enums)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::block_in_if_condition_expr)]
+#![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::box_vec)]
+#![warn(clippy::const_static_lifetime)]
+#![warn(clippy::cyclomatic_complexity)]
+#![warn(clippy::disallowed_method)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::eval_order_dependence)]
+#![warn(clippy::for_loop_over_option)]
+#![warn(clippy::for_loop_over_result)]
+#![warn(clippy::identity_conversion)]
+#![warn(clippy::if_let_some_result)]
+#![warn(clippy::new_without_default_derive)]
+#![warn(clippy::option_and_then_some)]
+#![warn(clippy::option_expect_used)]
+#![warn(clippy::option_map_unwrap_or)]
+#![warn(clippy::option_map_unwrap_or_else)]
+#![warn(clippy::option_unwrap_used)]
+#![warn(clippy::ref_in_deref)]
+#![warn(clippy::result_expect_used)]
+#![warn(clippy::result_map_unwrap_or_else)]
+#![warn(clippy::result_unwrap_used)]
+#![warn(clippy::single_char_push_str)]
+#![warn(clippy::stutter)]
+#![warn(clippy::to_string_in_display)]
+#![warn(clippy::zero_width_space)]
+#![warn(clippy::drop_bounds)]
+#![warn(clippy::into_iter_on_array)]
+#![warn(clippy::invalid_atomic_ordering)]
+#![warn(clippy::invalid_ref)]
+#![warn(clippy::mem_discriminant_non_enum)]
+#![warn(clippy::panic_params)]
+#![warn(clippy::temporary_cstring_as_ptr)]
+#![warn(clippy::unknown_clippy_lints)]
+#![warn(clippy::unused_label)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
new file mode 100644
index 000000000..8ea46b580
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -0,0 +1,214 @@
+error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
+ --> $DIR/rename.rs:36:9
+ |
+LL | #![warn(clippy::block_in_if_condition_expr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+ |
+ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+
+error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
+ --> $DIR/rename.rs:37:9
+ |
+LL | #![warn(clippy::block_in_if_condition_stmt)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+
+error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
+ --> $DIR/rename.rs:38:9
+ |
+LL | #![warn(clippy::box_vec)]
+ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+
+error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+ --> $DIR/rename.rs:39:9
+ |
+LL | #![warn(clippy::const_static_lifetime)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
+
+error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
+ --> $DIR/rename.rs:40:9
+ |
+LL | #![warn(clippy::cyclomatic_complexity)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
+
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+ --> $DIR/rename.rs:41:9
+ |
+LL | #![warn(clippy::disallowed_method)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+ --> $DIR/rename.rs:42:9
+ |
+LL | #![warn(clippy::disallowed_type)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+
+error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
+ --> $DIR/rename.rs:43:9
+ |
+LL | #![warn(clippy::eval_order_dependence)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
+
+error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
+ --> $DIR/rename.rs:44:9
+ |
+LL | #![warn(clippy::for_loop_over_option)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+
+error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
+ --> $DIR/rename.rs:45:9
+ |
+LL | #![warn(clippy::for_loop_over_result)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+
+error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
+ --> $DIR/rename.rs:46:9
+ |
+LL | #![warn(clippy::identity_conversion)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
+
+error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
+ --> $DIR/rename.rs:47:9
+ |
+LL | #![warn(clippy::if_let_some_result)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
+
+error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
+ --> $DIR/rename.rs:48:9
+ |
+LL | #![warn(clippy::new_without_default_derive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
+
+error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
+ --> $DIR/rename.rs:49:9
+ |
+LL | #![warn(clippy::option_and_then_some)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
+
+error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
+ --> $DIR/rename.rs:50:9
+ |
+LL | #![warn(clippy::option_expect_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
+
+error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
+ --> $DIR/rename.rs:51:9
+ |
+LL | #![warn(clippy::option_map_unwrap_or)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+
+error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+ --> $DIR/rename.rs:52:9
+ |
+LL | #![warn(clippy::option_map_unwrap_or_else)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+
+error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
+ --> $DIR/rename.rs:53:9
+ |
+LL | #![warn(clippy::option_unwrap_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+
+error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
+ --> $DIR/rename.rs:54:9
+ |
+LL | #![warn(clippy::ref_in_deref)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
+
+error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
+ --> $DIR/rename.rs:55:9
+ |
+LL | #![warn(clippy::result_expect_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
+
+error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+ --> $DIR/rename.rs:56:9
+ |
+LL | #![warn(clippy::result_map_unwrap_or_else)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+
+error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
+ --> $DIR/rename.rs:57:9
+ |
+LL | #![warn(clippy::result_unwrap_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+
+error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
+ --> $DIR/rename.rs:58:9
+ |
+LL | #![warn(clippy::single_char_push_str)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
+
+error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
+ --> $DIR/rename.rs:59:9
+ |
+LL | #![warn(clippy::stutter)]
+ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
+
+error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
+ --> $DIR/rename.rs:60:9
+ |
+LL | #![warn(clippy::to_string_in_display)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
+
+error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
+ --> $DIR/rename.rs:61:9
+ |
+LL | #![warn(clippy::zero_width_space)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
+
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
+ --> $DIR/rename.rs:62:9
+ |
+LL | #![warn(clippy::drop_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
+
+error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
+ --> $DIR/rename.rs:63:9
+ |
+LL | #![warn(clippy::into_iter_on_array)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
+
+error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
+ --> $DIR/rename.rs:64:9
+ |
+LL | #![warn(clippy::invalid_atomic_ordering)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
+
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
+ --> $DIR/rename.rs:65:9
+ |
+LL | #![warn(clippy::invalid_ref)]
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
+
+error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
+ --> $DIR/rename.rs:66:9
+ |
+LL | #![warn(clippy::mem_discriminant_non_enum)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
+
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
+ --> $DIR/rename.rs:67:9
+ |
+LL | #![warn(clippy::panic_params)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
+
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
+ --> $DIR/rename.rs:68:9
+ |
+LL | #![warn(clippy::temporary_cstring_as_ptr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
+
+error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
+ --> $DIR/rename.rs:69:9
+ |
+LL | #![warn(clippy::unknown_clippy_lints)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
+
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
+ --> $DIR/rename.rs:70:9
+ |
+LL | #![warn(clippy::unused_label)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
+
+error: aborting due to 35 previous errors
+
diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed
new file mode 100644
index 000000000..cb91b841d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed
@@ -0,0 +1,4 @@
+// run-rustfix
+
+#[clippy::cognitive_complexity = "1"]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.rs b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs
new file mode 100644
index 000000000..b3ce27580
--- /dev/null
+++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs
@@ -0,0 +1,4 @@
+// run-rustfix
+
+#[clippy::cyclomatic_complexity = "1"]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr
new file mode 100644
index 000000000..880467624
--- /dev/null
+++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr
@@ -0,0 +1,8 @@
+error: usage of deprecated attribute
+ --> $DIR/renamed_builtin_attr.rs:3:11
+ |
+LL | #[clippy::cyclomatic_complexity = "1"]
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/repeat_once.fixed b/src/tools/clippy/tests/ui/repeat_once.fixed
new file mode 100644
index 000000000..dc197e503
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::repeat_once)]
+#[allow(unused, clippy::redundant_clone)]
+fn main() {
+ const N: usize = 1;
+ let s = "str";
+ let string = "String".to_string();
+ let slice = [1; 5];
+
+ let a = [1; 5].to_vec();
+ let b = slice.to_vec();
+ let c = "hello".to_string();
+ let d = "hi".to_string();
+ let e = s.to_string();
+ let f = string.clone();
+}
diff --git a/src/tools/clippy/tests/ui/repeat_once.rs b/src/tools/clippy/tests/ui/repeat_once.rs
new file mode 100644
index 000000000..0ec512711
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::repeat_once)]
+#[allow(unused, clippy::redundant_clone)]
+fn main() {
+ const N: usize = 1;
+ let s = "str";
+ let string = "String".to_string();
+ let slice = [1; 5];
+
+ let a = [1; 5].repeat(1);
+ let b = slice.repeat(1);
+ let c = "hello".repeat(N);
+ let d = "hi".repeat(1);
+ let e = s.repeat(1);
+ let f = string.repeat(1);
+}
diff --git a/src/tools/clippy/tests/ui/repeat_once.stderr b/src/tools/clippy/tests/ui/repeat_once.stderr
new file mode 100644
index 000000000..915eea3bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.stderr
@@ -0,0 +1,40 @@
+error: calling `repeat(1)` on slice
+ --> $DIR/repeat_once.rs:10:13
+ |
+LL | let a = [1; 5].repeat(1);
+ | ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()`
+ |
+ = note: `-D clippy::repeat-once` implied by `-D warnings`
+
+error: calling `repeat(1)` on slice
+ --> $DIR/repeat_once.rs:11:13
+ |
+LL | let b = slice.repeat(1);
+ | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()`
+
+error: calling `repeat(1)` on str
+ --> $DIR/repeat_once.rs:12:13
+ |
+LL | let c = "hello".repeat(N);
+ | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()`
+
+error: calling `repeat(1)` on str
+ --> $DIR/repeat_once.rs:13:13
+ |
+LL | let d = "hi".repeat(1);
+ | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()`
+
+error: calling `repeat(1)` on str
+ --> $DIR/repeat_once.rs:14:13
+ |
+LL | let e = s.repeat(1);
+ | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()`
+
+error: calling `repeat(1)` on a string literal
+ --> $DIR/repeat_once.rs:15:13
+ |
+LL | let f = string.repeat(1);
+ | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/repl_uninit.rs b/src/tools/clippy/tests/ui/repl_uninit.rs
new file mode 100644
index 000000000..6c7e2b854
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repl_uninit.rs
@@ -0,0 +1,41 @@
+#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)]
+#![warn(clippy::mem_replace_with_uninit)]
+
+use std::mem;
+
+fn might_panic<X>(x: X) -> X {
+ // in practice this would be a possibly-panicky operation
+ x
+}
+
+fn main() {
+ let mut v = vec![0i32; 4];
+ // the following is UB if `might_panic` panics
+ unsafe {
+ let taken_v = mem::replace(&mut v, mem::uninitialized());
+ let new_v = might_panic(taken_v);
+ std::mem::forget(mem::replace(&mut v, new_v));
+ }
+
+ unsafe {
+ let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init());
+ let new_v = might_panic(taken_v);
+ std::mem::forget(mem::replace(&mut v, new_v));
+ }
+
+ unsafe {
+ let taken_v = mem::replace(&mut v, mem::zeroed());
+ let new_v = might_panic(taken_v);
+ std::mem::forget(mem::replace(&mut v, new_v));
+ }
+
+ // this is silly but OK, because usize is a primitive type
+ let mut u: usize = 42;
+ let uref = &mut u;
+ let taken_u = unsafe { mem::replace(uref, mem::zeroed()) };
+ *uref = taken_u + 1;
+
+ // this is still not OK, because uninit
+ let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
+ *uref = taken_u + 1;
+}
diff --git a/src/tools/clippy/tests/ui/repl_uninit.stderr b/src/tools/clippy/tests/ui/repl_uninit.stderr
new file mode 100644
index 000000000..09468eeae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repl_uninit.stderr
@@ -0,0 +1,30 @@
+error: replacing with `mem::uninitialized()`
+ --> $DIR/repl_uninit.rs:15:23
+ |
+LL | let taken_v = mem::replace(&mut v, mem::uninitialized());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)`
+ |
+ = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings`
+
+error: replacing with `mem::MaybeUninit::uninit().assume_init()`
+ --> $DIR/repl_uninit.rs:21:23
+ |
+LL | let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)`
+
+error: replacing with `mem::zeroed()`
+ --> $DIR/repl_uninit.rs:27:23
+ |
+LL | let taken_v = mem::replace(&mut v, mem::zeroed());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a default value or the `take_mut` crate instead
+
+error: replacing with `mem::uninitialized()`
+ --> $DIR/repl_uninit.rs:39:28
+ |
+LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(uref)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
new file mode 100644
index 000000000..086331af6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
@@ -0,0 +1,57 @@
+#![warn(clippy::rest_pat_in_fully_bound_structs)]
+
+struct A {
+ a: i32,
+ b: i64,
+ c: &'static str,
+}
+
+macro_rules! foo {
+ ($param:expr) => {
+ match $param {
+ A { a: 0, b: 0, c: "", .. } => {},
+ _ => {},
+ }
+ };
+}
+
+fn main() {
+ let a_struct = A { a: 5, b: 42, c: "A" };
+
+ match a_struct {
+ A { a: 5, b: 42, c: "", .. } => {}, // Lint
+ A { a: 0, b: 0, c: "", .. } => {}, // Lint
+ _ => {},
+ }
+
+ match a_struct {
+ A { a: 5, b: 42, .. } => {},
+ A { a: 0, b: 0, c: "", .. } => {}, // Lint
+ _ => {},
+ }
+
+ // No lint
+ match a_struct {
+ A { a: 5, .. } => {},
+ A { a: 0, b: 0, .. } => {},
+ _ => {},
+ }
+
+ // No lint
+ foo!(a_struct);
+
+ #[non_exhaustive]
+ struct B {
+ a: u32,
+ b: u32,
+ c: u64,
+ }
+
+ let b_struct = B { a: 5, b: 42, c: 342 };
+
+ match b_struct {
+ B { a: 5, b: 42, .. } => {},
+ B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
+ _ => {},
+ }
+}
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
new file mode 100644
index 000000000..57ebd47f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
@@ -0,0 +1,27 @@
+error: unnecessary use of `..` pattern in struct binding. All fields were already bound
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9
+ |
+LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings`
+ = help: consider removing `..` from this binding
+
+error: unnecessary use of `..` pattern in struct binding. All fields were already bound
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9
+ |
+LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `..` from this binding
+
+error: unnecessary use of `..` pattern in struct binding. All fields were already bound
+ --> $DIR/rest_pat_in_fully_bound_structs.rs:29:9
+ |
+LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `..` from this binding
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.fixed b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed
new file mode 100644
index 000000000..331531b51
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed
@@ -0,0 +1,19 @@
+// run-rustfix
+
+#![warn(clippy::result_map_or_into_option)]
+
+fn main() {
+ let opt: Result<u32, &str> = Ok(1);
+ let _ = opt.ok();
+
+ let rewrap = |s: u32| -> Option<u32> { Some(s) };
+
+ // A non-Some `f` arg should not emit the lint
+ let opt: Result<u32, &str> = Ok(1);
+ let _ = opt.map_or(None, rewrap);
+
+ // A non-Some `f` closure where the argument is not used as the
+ // return should not emit the lint
+ let opt: Result<u32, &str> = Ok(1);
+ opt.map_or(None, |_x| Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.rs b/src/tools/clippy/tests/ui/result_map_or_into_option.rs
new file mode 100644
index 000000000..3058480e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_or_into_option.rs
@@ -0,0 +1,19 @@
+// run-rustfix
+
+#![warn(clippy::result_map_or_into_option)]
+
+fn main() {
+ let opt: Result<u32, &str> = Ok(1);
+ let _ = opt.map_or(None, Some);
+
+ let rewrap = |s: u32| -> Option<u32> { Some(s) };
+
+ // A non-Some `f` arg should not emit the lint
+ let opt: Result<u32, &str> = Ok(1);
+ let _ = opt.map_or(None, rewrap);
+
+ // A non-Some `f` closure where the argument is not used as the
+ // return should not emit the lint
+ let opt: Result<u32, &str> = Ok(1);
+ opt.map_or(None, |_x| Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.stderr b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr
new file mode 100644
index 000000000..febf32147
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr
@@ -0,0 +1,10 @@
+error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
+ --> $DIR/result_map_or_into_option.rs:7:13
+ |
+LL | let _ = opt.map_or(None, Some);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()`
+ |
+ = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed
new file mode 100644
index 000000000..14c331f67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed
@@ -0,0 +1,82 @@
+// run-rustfix
+
+#![warn(clippy::result_map_unit_fn)]
+#![allow(unused)]
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+struct HasResult {
+ field: Result<usize, usize>,
+}
+
+impl HasResult {
+ fn do_result_nothing(&self, value: usize) {}
+
+ fn do_result_plus_one(&self, value: usize) -> usize {
+ value + 1
+ }
+}
+
+#[rustfmt::skip]
+fn result_map_unit_fn() {
+ let x = HasResult { field: Ok(10) };
+
+ x.field.map(plus_one);
+ let _: Result<(), usize> = x.field.map(do_nothing);
+
+ if let Ok(x_field) = x.field { do_nothing(x_field) }
+
+ if let Ok(x_field) = x.field { do_nothing(x_field) }
+
+ if let Ok(x_field) = x.field { diverge(x_field) }
+
+ let captured = 10;
+ if let Ok(value) = x.field { do_nothing(value + captured) };
+ let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
+
+ if let Ok(value) = x.field { x.do_result_nothing(value + captured) }
+
+ if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }
+
+
+ if let Ok(value) = x.field { do_nothing(value + captured) }
+
+ if let Ok(value) = x.field { do_nothing(value + captured) }
+
+ if let Ok(value) = x.field { do_nothing(value + captured); }
+
+ if let Ok(value) = x.field { do_nothing(value + captured); }
+
+
+ if let Ok(value) = x.field { diverge(value + captured) }
+
+ if let Ok(value) = x.field { diverge(value + captured) }
+
+ if let Ok(value) = x.field { diverge(value + captured); }
+
+ if let Ok(value) = x.field { diverge(value + captured); }
+
+
+ x.field.map(|value| plus_one(value + captured));
+ x.field.map(|value| { plus_one(value + captured) });
+ if let Ok(value) = x.field { let y = plus_one(value + captured); }
+
+ if let Ok(value) = x.field { plus_one(value + captured); }
+
+ if let Ok(value) = x.field { plus_one(value + captured); }
+
+
+ if let Ok(ref value) = x.field { do_nothing(value + captured) }
+
+ if let Ok(value) = x.field { println!("{:?}", value) }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs
new file mode 100644
index 000000000..8b0fca9ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs
@@ -0,0 +1,82 @@
+// run-rustfix
+
+#![warn(clippy::result_map_unit_fn)]
+#![allow(unused)]
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+struct HasResult {
+ field: Result<usize, usize>,
+}
+
+impl HasResult {
+ fn do_result_nothing(&self, value: usize) {}
+
+ fn do_result_plus_one(&self, value: usize) -> usize {
+ value + 1
+ }
+}
+
+#[rustfmt::skip]
+fn result_map_unit_fn() {
+ let x = HasResult { field: Ok(10) };
+
+ x.field.map(plus_one);
+ let _: Result<(), usize> = x.field.map(do_nothing);
+
+ x.field.map(do_nothing);
+
+ x.field.map(do_nothing);
+
+ x.field.map(diverge);
+
+ let captured = 10;
+ if let Ok(value) = x.field { do_nothing(value + captured) };
+ let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
+
+ x.field.map(|value| x.do_result_nothing(value + captured));
+
+ x.field.map(|value| { x.do_result_plus_one(value + captured); });
+
+
+ x.field.map(|value| do_nothing(value + captured));
+
+ x.field.map(|value| { do_nothing(value + captured) });
+
+ x.field.map(|value| { do_nothing(value + captured); });
+
+ x.field.map(|value| { { do_nothing(value + captured); } });
+
+
+ x.field.map(|value| diverge(value + captured));
+
+ x.field.map(|value| { diverge(value + captured) });
+
+ x.field.map(|value| { diverge(value + captured); });
+
+ x.field.map(|value| { { diverge(value + captured); } });
+
+
+ x.field.map(|value| plus_one(value + captured));
+ x.field.map(|value| { plus_one(value + captured) });
+ x.field.map(|value| { let y = plus_one(value + captured); });
+
+ x.field.map(|value| { plus_one(value + captured); });
+
+ x.field.map(|value| { { plus_one(value + captured); } });
+
+
+ x.field.map(|ref value| { do_nothing(value + captured) });
+
+ x.field.map(|value| println!("{:?}", value));
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr
new file mode 100644
index 000000000..782febd52
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr
@@ -0,0 +1,148 @@
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:35:5
+ |
+LL | x.field.map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
+ |
+ = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
+
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:37:5
+ |
+LL | x.field.map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
+
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:39:5
+ |
+LL | x.field.map(diverge);
+ | ^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:45:5
+ |
+LL | x.field.map(|value| x.do_result_nothing(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:47:5
+ |
+LL | x.field.map(|value| { x.do_result_plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:50:5
+ |
+LL | x.field.map(|value| do_nothing(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:52:5
+ |
+LL | x.field.map(|value| { do_nothing(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:54:5
+ |
+LL | x.field.map(|value| { do_nothing(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:56:5
+ |
+LL | x.field.map(|value| { { do_nothing(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:59:5
+ |
+LL | x.field.map(|value| diverge(value + captured));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:61:5
+ |
+LL | x.field.map(|value| { diverge(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:63:5
+ |
+LL | x.field.map(|value| { diverge(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:65:5
+ |
+LL | x.field.map(|value| { { diverge(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:70:5
+ |
+LL | x.field.map(|value| { let y = plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:72:5
+ |
+LL | x.field.map(|value| { plus_one(value + captured); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:74:5
+ |
+LL | x.field.map(|value| { { plus_one(value + captured); } });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:77:5
+ |
+LL | x.field.map(|ref value| { do_nothing(value + captured) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_fixable.rs:79:5
+ |
+LL | x.field.map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs
new file mode 100644
index 000000000..b197c609d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs
@@ -0,0 +1,46 @@
+#![warn(clippy::result_map_unit_fn)]
+#![feature(never_type)]
+#![allow(unused)]
+
+struct HasResult {
+ field: Result<usize, usize>,
+}
+
+fn do_nothing<T>(_: T) {}
+
+fn diverge<T>(_: T) -> ! {
+ panic!()
+}
+
+fn plus_one(value: usize) -> usize {
+ value + 1
+}
+
+#[rustfmt::skip]
+fn result_map_unit_fn() {
+ let x = HasResult { field: Ok(10) };
+
+ x.field.map(|value| { do_nothing(value); do_nothing(value) });
+
+ x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
+
+ // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
+ // proper suggestion for these cases
+ x.field.map(|value| {
+ do_nothing(value);
+ do_nothing(value)
+ });
+ x.field.map(|value| { do_nothing(value); do_nothing(value); });
+
+ // The following should suggest `if let Ok(_X) ...` as it's difficult to generate a proper let variable name for them
+ let res: Result<!, usize> = Ok(42).map(diverge);
+ "12".parse::<i32>().map(diverge);
+
+ let res: Result<(), usize> = Ok(plus_one(1)).map(do_nothing);
+
+ // Should suggest `if let Ok(_y) ...` to not override the existing foo variable
+ let y: Result<usize, usize> = Ok(42);
+ y.map(do_nothing);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
new file mode 100644
index 000000000..88e4efdb0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
@@ -0,0 +1,58 @@
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:23:5
+ |
+LL | x.field.map(|value| { do_nothing(value); do_nothing(value) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { ... }`
+ |
+ = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:25:5
+ |
+LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { ... }`
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:29:5
+ |
+LL | x.field.map(|value| {
+ | _____^
+ | |_____|
+ | ||
+LL | || do_nothing(value);
+LL | || do_nothing(value)
+LL | || });
+ | ||______^- help: try this: `if let Ok(value) = x.field { ... }`
+ | |_______|
+ |
+
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:33:5
+ |
+LL | x.field.map(|value| { do_nothing(value); do_nothing(value); });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { ... }`
+
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:37:5
+ |
+LL | "12".parse::<i32>().map(diverge);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(a) = "12".parse::<i32>() { diverge(a) }`
+
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
+ --> $DIR/result_map_unit_fn_unfixable.rs:43:5
+ |
+LL | y.map(do_nothing);
+ | ^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(_y) = y { do_nothing(_y) }`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/result_unit_error.rs b/src/tools/clippy/tests/ui/result_unit_error.rs
new file mode 100644
index 000000000..a4ec80302
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_unit_error.rs
@@ -0,0 +1,56 @@
+#![warn(clippy::result_unit_err)]
+
+pub fn returns_unit_error() -> Result<u32, ()> {
+ Err(())
+}
+
+fn private_unit_errors() -> Result<String, ()> {
+ Err(())
+}
+
+pub trait HasUnitError {
+ fn get_that_error(&self) -> Result<bool, ()>;
+
+ fn get_this_one_too(&self) -> Result<bool, ()> {
+ Err(())
+ }
+}
+
+impl HasUnitError for () {
+ fn get_that_error(&self) -> Result<bool, ()> {
+ Ok(true)
+ }
+}
+
+trait PrivateUnitError {
+ fn no_problem(&self) -> Result<usize, ()>;
+}
+
+pub struct UnitErrorHolder;
+
+impl UnitErrorHolder {
+ pub fn unit_error(&self) -> Result<usize, ()> {
+ Ok(0)
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/6546
+pub mod issue_6546 {
+ type ResInv<A, B> = Result<B, A>;
+
+ pub fn should_lint() -> ResInv<(), usize> {
+ Ok(0)
+ }
+
+ pub fn should_not_lint() -> ResInv<usize, ()> {
+ Ok(())
+ }
+
+ type MyRes<A, B> = Result<(A, B), Box<dyn std::error::Error>>;
+
+ pub fn should_not_lint2(x: i32) -> MyRes<i32, ()> {
+ Ok((x, ()))
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_unit_error.stderr b/src/tools/clippy/tests/ui/result_unit_error.stderr
new file mode 100644
index 000000000..8c7573eab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/result_unit_error.stderr
@@ -0,0 +1,43 @@
+error: this returns a `Result<_, ()>`
+ --> $DIR/result_unit_error.rs:3:1
+ |
+LL | pub fn returns_unit_error() -> Result<u32, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::result-unit-err` implied by `-D warnings`
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/result_unit_error.rs:12:5
+ |
+LL | fn get_that_error(&self) -> Result<bool, ()>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/result_unit_error.rs:14:5
+ |
+LL | fn get_this_one_too(&self) -> Result<bool, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/result_unit_error.rs:32:5
+ |
+LL | pub fn unit_error(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: this returns a `Result<_, ()>`
+ --> $DIR/result_unit_error.rs:41:5
+ |
+LL | pub fn should_lint() -> ResInv<(), usize> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a custom `Error` type instead
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/return_self_not_must_use.rs b/src/tools/clippy/tests/ui/return_self_not_must_use.rs
new file mode 100644
index 000000000..9b33ad6d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/return_self_not_must_use.rs
@@ -0,0 +1,58 @@
+#![crate_type = "lib"]
+#![warn(clippy::return_self_not_must_use)]
+
+#[derive(Clone)]
+pub struct Bar;
+
+pub trait Whatever {
+ fn what(&self) -> Self;
+ // There should be no warning here! (returns a reference)
+ fn what2(&self) -> &Self;
+}
+
+impl Bar {
+ // There should be no warning here! (note taking a self argument)
+ pub fn not_new() -> Self {
+ Self
+ }
+ pub fn foo(&self) -> Self {
+ Self
+ }
+ pub fn bar(self) -> Self {
+ self
+ }
+ // There should be no warning here! (private method)
+ fn foo2(&self) -> Self {
+ Self
+ }
+ // There should be no warning here! (returns a reference)
+ pub fn foo3(&self) -> &Self {
+ self
+ }
+ // There should be no warning here! (already a `must_use` attribute)
+ #[must_use]
+ pub fn foo4(&self) -> Self {
+ Self
+ }
+}
+
+impl Whatever for Bar {
+ // There should be no warning here! (comes from the trait)
+ fn what(&self) -> Self {
+ self.foo2()
+ }
+ // There should be no warning here! (comes from the trait)
+ fn what2(&self) -> &Self {
+ self
+ }
+}
+
+#[must_use]
+pub struct Foo;
+
+impl Foo {
+ // There should be no warning here! (`Foo` already implements `#[must_use]`)
+ fn foo(&self) -> Self {
+ Self
+ }
+}
diff --git a/src/tools/clippy/tests/ui/return_self_not_must_use.stderr b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr
new file mode 100644
index 000000000..94be87dfa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr
@@ -0,0 +1,31 @@
+error: missing `#[must_use]` attribute on a method returning `Self`
+ --> $DIR/return_self_not_must_use.rs:8:5
+ |
+LL | fn what(&self) -> Self;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::return-self-not-must-use` implied by `-D warnings`
+ = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
+
+error: missing `#[must_use]` attribute on a method returning `Self`
+ --> $DIR/return_self_not_must_use.rs:18:5
+ |
+LL | / pub fn foo(&self) -> Self {
+LL | | Self
+LL | | }
+ | |_____^
+ |
+ = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
+
+error: missing `#[must_use]` attribute on a method returning `Self`
+ --> $DIR/return_self_not_must_use.rs:21:5
+ |
+LL | / pub fn bar(self) -> Self {
+LL | | self
+LL | | }
+ | |_____^
+ |
+ = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed
new file mode 100644
index 000000000..79e482eec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+#![warn(clippy::reversed_empty_ranges)]
+
+const ANSWER: i32 = 42;
+
+fn main() {
+ // These should be linted:
+
+ (21..=42).rev().for_each(|x| println!("{}", x));
+ let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+
+ for _ in (-42..=-21).rev() {}
+ for _ in (21u32..42u32).rev() {}
+
+ // These should be ignored as they are not empty ranges:
+
+ (21..=42).for_each(|x| println!("{}", x));
+ (21..42).for_each(|x| println!("{}", x));
+
+ let arr = [1, 2, 3, 4, 5];
+ let _ = &arr[1..=3];
+ let _ = &arr[1..3];
+
+ for _ in 21..=42 {}
+ for _ in 21..42 {}
+
+ // This range is empty but should be ignored, see issue #5689
+ let _ = &arr[0..0];
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs
new file mode 100644
index 000000000..b2e8bf337
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+#![warn(clippy::reversed_empty_ranges)]
+
+const ANSWER: i32 = 42;
+
+fn main() {
+ // These should be linted:
+
+ (42..=21).for_each(|x| println!("{}", x));
+ let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+
+ for _ in -21..=-42 {}
+ for _ in 42u32..21u32 {}
+
+ // These should be ignored as they are not empty ranges:
+
+ (21..=42).for_each(|x| println!("{}", x));
+ (21..42).for_each(|x| println!("{}", x));
+
+ let arr = [1, 2, 3, 4, 5];
+ let _ = &arr[1..=3];
+ let _ = &arr[1..3];
+
+ for _ in 21..=42 {}
+ for _ in 21..42 {}
+
+ // This range is empty but should be ignored, see issue #5689
+ let _ = &arr[0..0];
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr
new file mode 100644
index 000000000..2d1bfe62c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr
@@ -0,0 +1,47 @@
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_fixable.rs:9:5
+ |
+LL | (42..=21).for_each(|x| println!("{}", x));
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings`
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | (21..=42).rev().for_each(|x| println!("{}", x));
+ | ~~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_fixable.rs:10:13
+ |
+LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+ | ^^^^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
+ | ~~~~~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_fixable.rs:12:14
+ |
+LL | for _ in -21..=-42 {}
+ | ^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for _ in (-42..=-21).rev() {}
+ | ~~~~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_fixable.rs:13:14
+ |
+LL | for _ in 42u32..21u32 {}
+ | ^^^^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for _ in (21u32..42u32).rev() {}
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed
new file mode 100644
index 000000000..f1503ed6d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed
@@ -0,0 +1,57 @@
+// run-rustfix
+#![warn(clippy::reversed_empty_ranges)]
+
+fn main() {
+ const MAX_LEN: usize = 42;
+
+ for i in (0..10).rev() {
+ println!("{}", i);
+ }
+
+ for i in (0..=10).rev() {
+ println!("{}", i);
+ }
+
+ for i in (0..MAX_LEN).rev() {
+ println!("{}", i);
+ }
+
+ for i in 5..=5 {
+ // not an error, this is the range with only one element “5”
+ println!("{}", i);
+ }
+
+ for i in 0..10 {
+ // not an error, the start index is less than the end index
+ println!("{}", i);
+ }
+
+ for i in -10..0 {
+ // not an error
+ println!("{}", i);
+ }
+
+ for i in (0..10).rev().map(|x| x * 2) {
+ println!("{}", i);
+ }
+
+ // testing that the empty range lint folds constants
+ for i in (5 + 4..10).rev() {
+ println!("{}", i);
+ }
+
+ for i in ((3 - 1)..(5 + 2)).rev() {
+ println!("{}", i);
+ }
+
+ for i in (2 * 2)..(2 * 3) {
+ // no error, 4..6 is fine
+ println!("{}", i);
+ }
+
+ let x = 42;
+ for i in x..10 {
+ // no error, not constant-foldable
+ println!("{}", i);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs
new file mode 100644
index 000000000..a733788dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.rs
@@ -0,0 +1,57 @@
+// run-rustfix
+#![warn(clippy::reversed_empty_ranges)]
+
+fn main() {
+ const MAX_LEN: usize = 42;
+
+ for i in 10..0 {
+ println!("{}", i);
+ }
+
+ for i in 10..=0 {
+ println!("{}", i);
+ }
+
+ for i in MAX_LEN..0 {
+ println!("{}", i);
+ }
+
+ for i in 5..=5 {
+ // not an error, this is the range with only one element “5”
+ println!("{}", i);
+ }
+
+ for i in 0..10 {
+ // not an error, the start index is less than the end index
+ println!("{}", i);
+ }
+
+ for i in -10..0 {
+ // not an error
+ println!("{}", i);
+ }
+
+ for i in (10..0).map(|x| x * 2) {
+ println!("{}", i);
+ }
+
+ // testing that the empty range lint folds constants
+ for i in 10..5 + 4 {
+ println!("{}", i);
+ }
+
+ for i in (5 + 2)..(3 - 1) {
+ println!("{}", i);
+ }
+
+ for i in (2 * 2)..(2 * 3) {
+ // no error, 4..6 is fine
+ println!("{}", i);
+ }
+
+ let x = 42;
+ for i in x..10 {
+ // no error, not constant-foldable
+ println!("{}", i);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr
new file mode 100644
index 000000000..a135da488
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr
@@ -0,0 +1,69 @@
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14
+ |
+LL | for i in 10..0 {
+ | ^^^^^
+ |
+ = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings`
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in (0..10).rev() {
+ | ~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14
+ |
+LL | for i in 10..=0 {
+ | ^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in (0..=10).rev() {
+ | ~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14
+ |
+LL | for i in MAX_LEN..0 {
+ | ^^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in (0..MAX_LEN).rev() {
+ | ~~~~~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14
+ |
+LL | for i in (10..0).map(|x| x * 2) {
+ | ^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in (0..10).rev().map(|x| x * 2) {
+ | ~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14
+ |
+LL | for i in 10..5 + 4 {
+ | ^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in (5 + 4..10).rev() {
+ | ~~~~~~~~~~~~~~~~~
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14
+ |
+LL | for i in (5 + 2)..(3 - 1) {
+ | ^^^^^^^^^^^^^^^^
+ |
+help: consider using the following if you are attempting to iterate over this range in reverse
+ |
+LL | for i in ((3 - 1)..(5 + 2)).rev() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs
new file mode 100644
index 000000000..c4c572244
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.rs
@@ -0,0 +1,11 @@
+#![warn(clippy::reversed_empty_ranges)]
+
+fn main() {
+ for i in 5..5 {
+ println!("{}", i);
+ }
+
+ for i in (5 + 2)..(8 - 1) {
+ println!("{}", i);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr
new file mode 100644
index 000000000..30095d20c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_unfixable.stderr
@@ -0,0 +1,16 @@
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14
+ |
+LL | for i in 5..5 {
+ | ^^^^
+ |
+ = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings`
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14
+ |
+LL | for i in (5 + 2)..(8 - 1) {
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs
new file mode 100644
index 000000000..264d3d1e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::reversed_empty_ranges)]
+
+const ANSWER: i32 = 42;
+const SOME_NUM: usize = 3;
+
+fn main() {
+ let arr = [1, 2, 3, 4, 5];
+ let _ = &arr[3usize..=1usize];
+ let _ = &arr[SOME_NUM..1];
+
+ for _ in ANSWER..ANSWER {}
+
+ // Should not be linted, see issue #5689
+ let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21);
+}
diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr
new file mode 100644
index 000000000..f23d4eb0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr
@@ -0,0 +1,22 @@
+error: this range is reversed and using it to index a slice will panic at run-time
+ --> $DIR/reversed_empty_ranges_unfixable.rs:8:18
+ |
+LL | let _ = &arr[3usize..=1usize];
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings`
+
+error: this range is reversed and using it to index a slice will panic at run-time
+ --> $DIR/reversed_empty_ranges_unfixable.rs:9:18
+ |
+LL | let _ = &arr[SOME_NUM..1];
+ | ^^^^^^^^^^^
+
+error: this range is empty so it will yield no values
+ --> $DIR/reversed_empty_ranges_unfixable.rs:11:14
+ |
+LL | for _ in ANSWER..ANSWER {}
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
new file mode 100644
index 000000000..3d2295912
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
@@ -0,0 +1,109 @@
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
+#![warn(clippy::same_functions_in_if_condition)]
+#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
+#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
+
+fn function() -> bool {
+ true
+}
+
+fn fn_arg(_arg: u8) -> bool {
+ true
+}
+
+struct Struct;
+
+impl Struct {
+ fn method(&self) -> bool {
+ true
+ }
+ fn method_arg(&self, _arg: u8) -> bool {
+ true
+ }
+}
+
+fn ifs_same_cond_fn() {
+ let a = 0;
+ let obj = Struct;
+
+ if function() {
+ } else if function() {
+ //~ ERROR ifs same condition
+ }
+
+ if fn_arg(a) {
+ } else if fn_arg(a) {
+ //~ ERROR ifs same condition
+ }
+
+ if obj.method() {
+ } else if obj.method() {
+ //~ ERROR ifs same condition
+ }
+
+ if obj.method_arg(a) {
+ } else if obj.method_arg(a) {
+ //~ ERROR ifs same condition
+ }
+
+ let mut v = vec![1];
+ if v.pop() == None {
+ //~ ERROR ifs same condition
+ } else if v.pop() == None {
+ }
+
+ if v.len() == 42 {
+ //~ ERROR ifs same condition
+ } else if v.len() == 42 {
+ }
+
+ if v.len() == 1 {
+ // ok, different conditions
+ } else if v.len() == 2 {
+ }
+
+ if fn_arg(0) {
+ // ok, different arguments.
+ } else if fn_arg(1) {
+ }
+
+ if obj.method_arg(0) {
+ // ok, different arguments.
+ } else if obj.method_arg(1) {
+ }
+
+ if a == 1 {
+ // ok, warning is on `ifs_same_cond` behalf.
+ } else if a == 1 {
+ }
+}
+
+fn main() {
+ // macro as condition (see #6168)
+ let os = if cfg!(target_os = "macos") {
+ "macos"
+ } else if cfg!(target_os = "windows") {
+ "windows"
+ } else {
+ "linux"
+ };
+ println!("{}", os);
+
+ #[derive(PartialEq, Eq)]
+ enum E {
+ A,
+ B,
+ }
+ fn generic<const P: E>() -> bool {
+ match P {
+ E::A => true,
+ E::B => false,
+ }
+ }
+ if generic::<{ E::A }>() {
+ println!("A");
+ } else if generic::<{ E::B }>() {
+ println!("B");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
new file mode 100644
index 000000000..71e82910e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
@@ -0,0 +1,75 @@
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:31:15
+ |
+LL | } else if function() {
+ | ^^^^^^^^^^
+ |
+ = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:30:8
+ |
+LL | if function() {
+ | ^^^^^^^^^^
+
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:36:15
+ |
+LL | } else if fn_arg(a) {
+ | ^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:35:8
+ |
+LL | if fn_arg(a) {
+ | ^^^^^^^^^
+
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:41:15
+ |
+LL | } else if obj.method() {
+ | ^^^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:40:8
+ |
+LL | if obj.method() {
+ | ^^^^^^^^^^^^
+
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:46:15
+ |
+LL | } else if obj.method_arg(a) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:45:8
+ |
+LL | if obj.method_arg(a) {
+ | ^^^^^^^^^^^^^^^^^
+
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:53:15
+ |
+LL | } else if v.pop() == None {
+ | ^^^^^^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:51:8
+ |
+LL | if v.pop() == None {
+ | ^^^^^^^^^^^^^^^
+
+error: this `if` has the same function call as a previous `if`
+ --> $DIR/same_functions_in_if_condition.rs:58:15
+ |
+LL | } else if v.len() == 42 {
+ | ^^^^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/same_functions_in_if_condition.rs:56:8
+ |
+LL | if v.len() == 42 {
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs
new file mode 100644
index 000000000..99964f0de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_item_push.rs
@@ -0,0 +1,158 @@
+#![warn(clippy::same_item_push)]
+
+const VALUE: u8 = 7;
+
+fn mutate_increment(x: &mut u8) -> u8 {
+ *x += 1;
+ *x
+}
+
+fn increment(x: u8) -> u8 {
+ x + 1
+}
+
+fn fun() -> usize {
+ 42
+}
+
+fn main() {
+ // ** linted cases **
+ let mut vec: Vec<u8> = Vec::new();
+ let item = 2;
+ for _ in 5..=20 {
+ vec.push(item);
+ }
+
+ let mut vec: Vec<u8> = Vec::new();
+ for _ in 0..15 {
+ let item = 2;
+ vec.push(item);
+ }
+
+ let mut vec: Vec<u8> = Vec::new();
+ for _ in 0..15 {
+ vec.push(13);
+ }
+
+ let mut vec = Vec::new();
+ for _ in 0..20 {
+ vec.push(VALUE);
+ }
+
+ let mut vec = Vec::new();
+ let item = VALUE;
+ for _ in 0..20 {
+ vec.push(item);
+ }
+
+ // ** non-linted cases **
+ let mut spaces = Vec::with_capacity(10);
+ for _ in 0..10 {
+ spaces.push(vec![b' ']);
+ }
+
+ // Suggestion should not be given as pushed variable can mutate
+ let mut vec: Vec<u8> = Vec::new();
+ let mut item: u8 = 2;
+ for _ in 0..30 {
+ vec.push(mutate_increment(&mut item));
+ }
+
+ let mut vec: Vec<u8> = Vec::new();
+ let mut item: u8 = 2;
+ let mut item2 = &mut mutate_increment(&mut item);
+ for _ in 0..30 {
+ vec.push(mutate_increment(item2));
+ }
+
+ let mut vec: Vec<usize> = Vec::new();
+ for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
+ vec.push(a);
+ }
+
+ let mut vec: Vec<u8> = Vec::new();
+ for i in 0..30 {
+ vec.push(increment(i));
+ }
+
+ let mut vec: Vec<u8> = Vec::new();
+ for i in 0..30 {
+ vec.push(i + i * i);
+ }
+
+ // Suggestion should not be given as there are multiple pushes that are not the same
+ let mut vec: Vec<u8> = Vec::new();
+ let item: u8 = 2;
+ for _ in 0..30 {
+ vec.push(item);
+ vec.push(item * 2);
+ }
+
+ // Suggestion should not be given as Vec is not involved
+ for _ in 0..5 {
+ println!("Same Item Push");
+ }
+
+ struct A {
+ kind: u32,
+ }
+ let mut vec_a: Vec<A> = Vec::new();
+ for i in 0..30 {
+ vec_a.push(A { kind: i });
+ }
+ let mut vec: Vec<u8> = Vec::new();
+ for a in vec_a {
+ vec.push(2u8.pow(a.kind));
+ }
+
+ // Fix #5902
+ let mut vec: Vec<u8> = Vec::new();
+ let mut item = 0;
+ for _ in 0..10 {
+ vec.push(item);
+ item += 10;
+ }
+
+ // Fix #5979
+ let mut vec: Vec<std::fs::File> = Vec::new();
+ for _ in 0..10 {
+ vec.push(std::fs::File::open("foobar").unwrap());
+ }
+ // Fix #5979
+ #[derive(Clone)]
+ struct S;
+
+ trait T {}
+ impl T for S {}
+
+ let mut vec: Vec<Box<dyn T>> = Vec::new();
+ for _ in 0..10 {
+ vec.push(Box::new(S {}));
+ }
+
+ // Fix #5985
+ let mut vec = Vec::new();
+ let item = 42;
+ let item = fun();
+ for _ in 0..20 {
+ vec.push(item);
+ }
+
+ // Fix #5985
+ let mut vec = Vec::new();
+ let key = 1;
+ for _ in 0..20 {
+ let item = match key {
+ 1 => 10,
+ _ => 0,
+ };
+ vec.push(item);
+ }
+
+ // Fix #6987
+ let mut vec = Vec::new();
+ for _ in 0..10 {
+ vec.push(1);
+ vec.extend(&[2]);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr
new file mode 100644
index 000000000..d9ffa1578
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_item_push.stderr
@@ -0,0 +1,43 @@
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:23:9
+ |
+LL | vec.push(item);
+ | ^^^
+ |
+ = note: `-D clippy::same-item-push` implied by `-D warnings`
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:29:9
+ |
+LL | vec.push(item);
+ | ^^^
+ |
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:34:9
+ |
+LL | vec.push(13);
+ | ^^^
+ |
+ = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:39:9
+ |
+LL | vec.push(VALUE);
+ | ^^^
+ |
+ = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:45:9
+ |
+LL | vec.push(item);
+ | ^^^
+ |
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/same_name_method.rs b/src/tools/clippy/tests/ui/same_name_method.rs
new file mode 100644
index 000000000..daef95a42
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_name_method.rs
@@ -0,0 +1,127 @@
+#![feature(lint_reasons)]
+#![warn(clippy::same_name_method)]
+#![allow(dead_code, non_camel_case_types)]
+
+trait T1 {
+ fn foo() {}
+}
+
+trait T2 {
+ fn foo() {}
+}
+
+mod should_lint {
+
+ mod test_basic_case {
+ use crate::T1;
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {
+ fn foo() {}
+ }
+ }
+
+ mod test_derive {
+
+ #[derive(Clone)]
+ struct S;
+
+ impl S {
+ fn clone() {}
+ }
+ }
+
+ mod with_generic {
+ use crate::T1;
+
+ struct S<U>(U);
+
+ impl<U> S<U> {
+ fn foo() {}
+ }
+
+ impl<U: Copy> T1 for S<U> {
+ fn foo() {}
+ }
+ }
+
+ mod default_method {
+ use crate::T1;
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {}
+ }
+
+ mod multiply_conflicit_trait {
+ use crate::{T1, T2};
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {}
+
+ impl T2 for S {}
+ }
+}
+
+mod should_not_lint {
+
+ mod not_lint_two_trait_method {
+ use crate::{T1, T2};
+
+ struct S;
+
+ impl T1 for S {
+ fn foo() {}
+ }
+
+ impl T2 for S {
+ fn foo() {}
+ }
+ }
+
+ mod only_lint_on_method {
+ trait T3 {
+ type foo;
+ }
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+ impl T3 for S {
+ type foo = usize;
+ }
+ }
+}
+
+mod check_expect_suppression {
+ use crate::T1;
+
+ struct S;
+
+ impl S {
+ #[expect(clippy::same_name_method)]
+ fn foo() {}
+ }
+
+ impl T1 for S {
+ fn foo() {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/same_name_method.stderr b/src/tools/clippy/tests/ui/same_name_method.stderr
new file mode 100644
index 000000000..f55ec9f3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/same_name_method.stderr
@@ -0,0 +1,64 @@
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:21:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::same-name-method` implied by `-D warnings`
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:25:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:35:13
+ |
+LL | fn clone() {}
+ | ^^^^^^^^^^^^^
+ |
+note: existing `clone` defined here
+ --> $DIR/same_name_method.rs:31:18
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:45:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:49:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:59:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:62:9
+ |
+LL | impl T1 for S {}
+ | ^^^^^^^^^^^^^^^^
+
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:71:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:74:9
+ |
+LL | impl T1 for S {}
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs
new file mode 100644
index 000000000..72f335153
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some.rs
@@ -0,0 +1,79 @@
+// aux-build:option_helpers.rs
+#![warn(clippy::search_is_some)]
+#![allow(dead_code)]
+extern crate option_helpers;
+use option_helpers::IteratorFalsePositives;
+
+#[rustfmt::skip]
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+
+ // Check `find().is_some()`, multi-line case.
+ let _ = v.iter().find(|&x| {
+ *x < 0
+ }
+ ).is_some();
+
+ // Check `position().is_some()`, multi-line case.
+ let _ = v.iter().position(|&x| {
+ x < 0
+ }
+ ).is_some();
+
+ // Check `rposition().is_some()`, multi-line case.
+ let _ = v.iter().rposition(|&x| {
+ x < 0
+ }
+ ).is_some();
+
+ // Check that we don't lint if the caller is not an `Iterator` or string
+ let falsepos = IteratorFalsePositives { foo: 0 };
+ let _ = falsepos.find().is_some();
+ let _ = falsepos.position().is_some();
+ let _ = falsepos.rposition().is_some();
+ // check that we don't lint if `find()` is called with
+ // `Pattern` that is not a string
+ let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
+
+ let some_closure = |x: &u32| *x == 0;
+ let _ = (0..1).find(some_closure).is_some();
+}
+
+#[rustfmt::skip]
+fn is_none() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+
+ // Check `find().is_none()`, multi-line case.
+ let _ = v.iter().find(|&x| {
+ *x < 0
+ }
+ ).is_none();
+
+ // Check `position().is_none()`, multi-line case.
+ let _ = v.iter().position(|&x| {
+ x < 0
+ }
+ ).is_none();
+
+ // Check `rposition().is_none()`, multi-line case.
+ let _ = v.iter().rposition(|&x| {
+ x < 0
+ }
+ ).is_none();
+
+ // Check that we don't lint if the caller is not an `Iterator` or string
+ let falsepos = IteratorFalsePositives { foo: 0 };
+ let _ = falsepos.find().is_none();
+ let _ = falsepos.position().is_none();
+ let _ = falsepos.rposition().is_none();
+ // check that we don't lint if `find()` is called with
+ // `Pattern` that is not a string
+ let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+
+ let some_closure = |x: &u32| *x == 0;
+ let _ = (0..1).find(some_closure).is_none();
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr
new file mode 100644
index 000000000..54760545b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some.stderr
@@ -0,0 +1,87 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:14:13
+ |
+LL | let _ = v.iter().find(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).is_some();
+ | |______________________________^
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+ = help: this is more succinctly expressed by calling `any()`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some.rs:20:13
+ |
+LL | let _ = v.iter().position(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_some();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some.rs:26:13
+ |
+LL | let _ = v.iter().rposition(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_some();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:41:20
+ |
+LL | let _ = (0..1).find(some_closure).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:51:13
+ |
+LL | let _ = v.iter().find(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some.rs:57:13
+ |
+LL | let _ = v.iter().position(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some.rs:63:13
+ |
+LL | let _ = v.iter().rposition(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:78:13
+ |
+LL | let _ = (0..1).find(some_closure).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
new file mode 100644
index 000000000..5190c5304
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
@@ -0,0 +1,216 @@
+// run-rustfix
+#![allow(dead_code, clippy::explicit_auto_deref)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = !v.iter().any(|x| *x < 0);
+ let _ = !(0..1).any(|x| **y == x); // one dereference less
+ let _ = !(0..1).any(|x| x == 0);
+ let _ = !v.iter().any(|x| *x == 0);
+ let _ = !(4..5).any(|x| x == 1 || x == 3 || x == 5);
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x));
+ let _ = !(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+ // Check `position().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = !"hello world".contains("world");
+ let _ = !"hello world".contains(&s2);
+ let _ = !"hello world".contains(&s2[2..]);
+ // caller of `find()` is a `String`
+ let _ = !s1.contains("world");
+ let _ = !s1.contains(&s2);
+ let _ = !s1.contains(&s2[2..]);
+ // caller of `find()` is slice of `String`
+ let _ = !s1[2..].contains("world");
+ let _ = !s1[2..].contains(&s2);
+ let _ = !s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| !filter_hand.iter().any(|cc| c == &cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| !filter_hand.iter().any(|cc| c == cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = !vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = !vfoo
+ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|a| a[0] == 42);
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|sub| sub[1..4].len() == 3);
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = ![ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+ let _ = ![String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+ let v = vec![3, 2, 1, 0];
+ let _ = !v.iter().any(|x| deref_enough(*x));
+ let _ = !v.iter().any(|x: &u32| deref_enough(*x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = !v.iter().any(|x| arg_no_deref(&x));
+ #[allow(clippy::redundant_closure)]
+ let _ = !v.iter().any(|x: &u32| arg_no_deref(&x));
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = !vfoo
+ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = !vfoo.iter().any(|v| v.inner[0].bar == 2);
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|x| (**x)[0] == 9);
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = !vfoo.iter().any(|v| v.by_ref(&v.bar));
+ }
+
+ fn ref_bindings() {
+ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = !v.iter().any(|s| s[0].is_empty());
+ let _ = !v.iter().any(|s| test_string_1(&s[0]));
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
+ let _ = !v.iter().any(|fp| test_u32_1(fp.field));
+ let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
new file mode 100644
index 000000000..310d87333
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
@@ -0,0 +1,222 @@
+// run-rustfix
+#![allow(dead_code, clippy::explicit_auto_deref)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = v.iter().find(|&x| *x < 0).is_none();
+ let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ let _ = (0..1).find(|x| *x == 0).is_none();
+ let _ = v.iter().find(|x| **x == 0).is_none();
+ let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+ let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+ let _ = (1..3)
+ .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ .is_none();
+
+ // Check `position().is_none()`, single-line case.
+ let _ = v.iter().position(|&x| x < 0).is_none();
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".find("world").is_none();
+ let _ = "hello world".find(&s2).is_none();
+ let _ = "hello world".find(&s2[2..]).is_none();
+ // caller of `find()` is a `String`
+ let _ = s1.find("world").is_none();
+ let _ = s1.find(&s2).is_none();
+ let _ = s1.find(&s2[2..]).is_none();
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].find("world").is_none();
+ let _ = s1[2..].find(&s2).is_none();
+ let _ = s1[2..].find(&s2[2..]).is_none();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ .is_none();
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+ let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+ let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ .is_none();
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+ let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+ let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+ let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
new file mode 100644
index 000000000..933ce5cf4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
@@ -0,0 +1,285 @@
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:10:13
+ |
+LL | let _ = v.iter().find(|&x| *x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:11:13
+ |
+LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:12:13
+ |
+LL | let _ = (0..1).find(|x| *x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:13:13
+ |
+LL | let _ = v.iter().find(|x| **x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:14:13
+ |
+LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:15:13
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:16:13
+ |
+LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:17:13
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:18:13
+ |
+LL | let _ = (1..3)
+ | _____________^
+LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+LL | | .is_none();
+ | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some_fixable_none.rs:23:13
+ |
+LL | let _ = v.iter().position(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some_fixable_none.rs:26:13
+ |
+LL | let _ = v.iter().rposition(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:32:13
+ |
+LL | let _ = "hello world".find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:33:13
+ |
+LL | let _ = "hello world".find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:34:13
+ |
+LL | let _ = "hello world".find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:36:13
+ |
+LL | let _ = s1.find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:37:13
+ |
+LL | let _ = s1.find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:38:13
+ |
+LL | let _ = s1.find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:40:13
+ |
+LL | let _ = s1[2..].find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:41:13
+ |
+LL | let _ = s1[2..].find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:42:13
+ |
+LL | let _ = s1[2..].find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:58:25
+ |
+LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:74:30
+ |
+LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:85:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:88:17
+ |
+LL | let _ = vfoo
+ | _________________^
+LL | | .iter()
+LL | | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+LL | | .is_none();
+ | |______________________^
+ |
+help: use `!_.any()` instead
+ |
+LL ~ let _ = !vfoo
+LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:96:17
+ |
+LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:102:17
+ |
+LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:120:17
+ |
+LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:121:17
+ |
+LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:124:17
+ |
+LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:125:17
+ |
+LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:128:17
+ |
+LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:130:17
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:150:17
+ |
+LL | let _ = vfoo
+ | _________________^
+LL | | .iter()
+LL | | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+LL | | .is_none();
+ | |______________________^
+ |
+help: use `!_.any()` instead
+ |
+LL ~ let _ = !vfoo
+LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:166:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:171:17
+ |
+LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:184:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:188:17
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:189:17
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:208:17
+ |
+LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:209:17
+ |
+LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:218:17
+ |
+LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:219:17
+ |
+LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:220:17
+ |
+LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 43 previous errors
+
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
new file mode 100644
index 000000000..385a9986a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
@@ -0,0 +1,248 @@
+// run-rustfix
+#![allow(dead_code, clippy::explicit_auto_deref)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_some()`, single-line case.
+ let _ = v.iter().any(|x| *x < 0);
+ let _ = (0..1).any(|x| **y == x); // one dereference less
+ let _ = (0..1).any(|x| x == 0);
+ let _ = v.iter().any(|x| *x == 0);
+ let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
+ let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
+ let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+ let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+ let _ = (1..3)
+ .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+ // Check `position().is_some()`, single-line case.
+ let _ = v.iter().any(|&x| x < 0);
+
+ // Check `rposition().is_some()`, single-line case.
+ let _ = v.iter().any(|&x| x < 0);
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".contains("world");
+ let _ = "hello world".contains(&s2);
+ let _ = "hello world".contains(&s2[2..]);
+ // caller of `find()` is a `String`
+ let _ = s1.contains("world");
+ let _ = s1.contains(&s2);
+ let _ = s1.contains(&s2[2..]);
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].contains("world");
+ let _ = s1[2..].contains(&s2);
+ let _ = s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().any(|cc| c == &cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|a| a[0] == 42);
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+ let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().any(|x| deref_enough(*x));
+ let _ = v.iter().any(|x: &u32| deref_enough(*x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x| arg_no_deref(&x));
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|x| (**x)[0] == 9);
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().any(|s| s[0].is_empty());
+ let _ = v.iter().any(|s| test_string_1(&s[0]));
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().any(|fp| fp.field.is_power_of_two());
+ let _ = v.iter().any(|fp| test_u32_1(fp.field));
+ let _ = v.iter().any(|fp| test_u32_2(*fp.field));
+ }
+}
+
+mod issue9120 {
+ fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+ move |x: &&u32| **x == 78
+ }
+
+ fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+ Box::new(move |x: &&u32| **x == 78)
+ }
+
+ fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+ #[allow(clippy::redundant_closure)]
+ v.iter().any(|x: &u32| func(&x))
+ }
+
+ fn do_tests() {
+ let v = vec![3, 2, 1, 0];
+ let arg_no_deref_impl = make_arg_no_deref_impl();
+ let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref_impl(&x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref_dyn(&x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| (*arg_no_deref_dyn)(&x));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
new file mode 100644
index 000000000..67e190ee3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
@@ -0,0 +1,251 @@
+// run-rustfix
+#![allow(dead_code, clippy::explicit_auto_deref)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_some()`, single-line case.
+ let _ = v.iter().find(|&x| *x < 0).is_some();
+ let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+ let _ = (0..1).find(|x| *x == 0).is_some();
+ let _ = v.iter().find(|x| **x == 0).is_some();
+ let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+ let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+ let _ = (1..3)
+ .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ .is_some();
+
+ // Check `position().is_some()`, single-line case.
+ let _ = v.iter().position(|&x| x < 0).is_some();
+
+ // Check `rposition().is_some()`, single-line case.
+ let _ = v.iter().rposition(|&x| x < 0).is_some();
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".find("world").is_some();
+ let _ = "hello world".find(&s2).is_some();
+ let _ = "hello world".find(&s2[2..]).is_some();
+ // caller of `find()` is a `String`
+ let _ = s1.find("world").is_some();
+ let _ = s1.find(&s2).is_some();
+ let _ = s1.find(&s2[2..]).is_some();
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].find("world").is_some();
+ let _ = s1[2..].find(&s2).is_some();
+ let _ = s1[2..].find(&s2[2..]).is_some();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ .is_some();
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+ let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+ let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ .is_some();
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+ let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+ let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+ let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+ }
+}
+
+mod issue9120 {
+ fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+ move |x: &&u32| **x == 78
+ }
+
+ fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+ Box::new(move |x: &&u32| **x == 78)
+ }
+
+ fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+ #[allow(clippy::redundant_closure)]
+ v.iter().find(|x: &&u32| func(x)).is_some()
+ }
+
+ fn do_tests() {
+ let v = vec![3, 2, 1, 0];
+ let arg_no_deref_impl = make_arg_no_deref_impl();
+ let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
new file mode 100644
index 000000000..c5c3c92c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
@@ -0,0 +1,292 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:10:22
+ |
+LL | let _ = v.iter().find(|&x| *x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:11:20
+ |
+LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:12:20
+ |
+LL | let _ = (0..1).find(|x| *x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:13:22
+ |
+LL | let _ = v.iter().find(|x| **x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:14:20
+ |
+LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:15:20
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:16:20
+ |
+LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:17:20
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:19:10
+ |
+LL | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ | __________^
+LL | | .is_some();
+ | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some_fixable_some.rs:23:22
+ |
+LL | let _ = v.iter().position(|&x| x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some_fixable_some.rs:26:22
+ |
+LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:31:27
+ |
+LL | let _ = "hello world".find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:32:27
+ |
+LL | let _ = "hello world".find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:33:27
+ |
+LL | let _ = "hello world".find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:35:16
+ |
+LL | let _ = s1.find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:36:16
+ |
+LL | let _ = s1.find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:37:16
+ |
+LL | let _ = s1.find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:39:21
+ |
+LL | let _ = s1[2..].find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:40:21
+ |
+LL | let _ = s1[2..].find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:41:21
+ |
+LL | let _ = s1[2..].find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:57:44
+ |
+LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:73:49
+ |
+LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:84:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:89:14
+ |
+LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ | ______________^
+LL | | .is_some();
+ | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:95:29
+ |
+LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:101:29
+ |
+LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:119:30
+ |
+LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:120:50
+ |
+LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:123:26
+ |
+LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:124:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:127:26
+ |
+LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:129:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:151:14
+ |
+LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ | ______________^
+LL | | .is_some();
+ | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:165:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:170:29
+ |
+LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:183:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:187:55
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:188:55
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:207:26
+ |
+LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:208:26
+ |
+LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:217:26
+ |
+LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:218:26
+ |
+LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:219:26
+ |
+LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:234:18
+ |
+LL | v.iter().find(|x: &&u32| func(x)).is_some()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:243:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:246:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:249:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`
+
+error: aborting due to 47 previous errors
+
diff --git a/src/tools/clippy/tests/ui/self_assignment.rs b/src/tools/clippy/tests/ui/self_assignment.rs
new file mode 100644
index 000000000..ef6476229
--- /dev/null
+++ b/src/tools/clippy/tests/ui/self_assignment.rs
@@ -0,0 +1,67 @@
+#![warn(clippy::self_assignment)]
+
+pub struct S<'a> {
+ a: i32,
+ b: [i32; 10],
+ c: Vec<Vec<i32>>,
+ e: &'a mut i32,
+ f: &'a mut i32,
+}
+
+pub fn positives(mut a: usize, b: &mut u32, mut s: S) {
+ a = a;
+ *b = *b;
+ s = s;
+ s.a = s.a;
+ s.b[10] = s.b[5 + 5];
+ s.c[0][1] = s.c[0][1];
+ s.b[a] = s.b[a];
+ *s.e = *s.e;
+ s.b[a + 10] = s.b[10 + a];
+
+ let mut t = (0, 1);
+ t.1 = t.1;
+ t.0 = (t.0);
+}
+
+pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) {
+ dbg!(&a);
+ a = *b;
+ dbg!(&a);
+ s.b[1] += s.b[1];
+ s.b[1] = s.b[2];
+ s.c[1][0] = s.c[0][1];
+ s.b[a] = s.b[*b];
+ s.b[a + 10] = s.b[a + 11];
+ *s.e = *s.f;
+
+ let mut t = (0, 1);
+ t.0 = t.1;
+}
+
+#[allow(clippy::mixed_read_write_in_expression)]
+pub fn negatives_side_effects() {
+ let mut v = vec![1, 2, 3, 4, 5];
+ let mut i = 0;
+ v[{
+ i += 1;
+ i
+ }] = v[{
+ i += 1;
+ i
+ }];
+
+ fn next(n: &mut usize) -> usize {
+ let v = *n;
+ *n += 1;
+ v
+ }
+
+ let mut w = vec![1, 2, 3, 4, 5];
+ let mut i = 0;
+ let i = &mut i;
+ w[next(i)] = w[next(i)];
+ w[next(i)] = w[next(i)];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/self_assignment.stderr b/src/tools/clippy/tests/ui/self_assignment.stderr
new file mode 100644
index 000000000..826e0d0ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/self_assignment.stderr
@@ -0,0 +1,70 @@
+error: self-assignment of `a` to `a`
+ --> $DIR/self_assignment.rs:12:5
+ |
+LL | a = a;
+ | ^^^^^
+ |
+ = note: `-D clippy::self-assignment` implied by `-D warnings`
+
+error: self-assignment of `*b` to `*b`
+ --> $DIR/self_assignment.rs:13:5
+ |
+LL | *b = *b;
+ | ^^^^^^^
+
+error: self-assignment of `s` to `s`
+ --> $DIR/self_assignment.rs:14:5
+ |
+LL | s = s;
+ | ^^^^^
+
+error: self-assignment of `s.a` to `s.a`
+ --> $DIR/self_assignment.rs:15:5
+ |
+LL | s.a = s.a;
+ | ^^^^^^^^^
+
+error: self-assignment of `s.b[5 + 5]` to `s.b[10]`
+ --> $DIR/self_assignment.rs:16:5
+ |
+LL | s.b[10] = s.b[5 + 5];
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `s.c[0][1]` to `s.c[0][1]`
+ --> $DIR/self_assignment.rs:17:5
+ |
+LL | s.c[0][1] = s.c[0][1];
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `s.b[a]` to `s.b[a]`
+ --> $DIR/self_assignment.rs:18:5
+ |
+LL | s.b[a] = s.b[a];
+ | ^^^^^^^^^^^^^^^
+
+error: self-assignment of `*s.e` to `*s.e`
+ --> $DIR/self_assignment.rs:19:5
+ |
+LL | *s.e = *s.e;
+ | ^^^^^^^^^^^
+
+error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]`
+ --> $DIR/self_assignment.rs:20:5
+ |
+LL | s.b[a + 10] = s.b[10 + a];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `t.1` to `t.1`
+ --> $DIR/self_assignment.rs:23:5
+ |
+LL | t.1 = t.1;
+ | ^^^^^^^^^
+
+error: self-assignment of `(t.0)` to `t.0`
+ --> $DIR/self_assignment.rs:24:5
+ |
+LL | t.0 = (t.0);
+ | ^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/self_named_constructors.rs b/src/tools/clippy/tests/ui/self_named_constructors.rs
new file mode 100644
index 000000000..356f701c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/self_named_constructors.rs
@@ -0,0 +1,59 @@
+#![warn(clippy::self_named_constructors)]
+
+struct ShouldSpawn;
+struct ShouldNotSpawn;
+
+impl ShouldSpawn {
+ pub fn should_spawn() -> ShouldSpawn {
+ ShouldSpawn
+ }
+
+ fn should_not_spawn() -> ShouldNotSpawn {
+ ShouldNotSpawn
+ }
+}
+
+impl ShouldNotSpawn {
+ pub fn new() -> ShouldNotSpawn {
+ ShouldNotSpawn
+ }
+}
+
+struct ShouldNotSpawnWithTrait;
+
+trait ShouldNotSpawnTrait {
+ type Item;
+}
+
+impl ShouldNotSpawnTrait for ShouldNotSpawnWithTrait {
+ type Item = Self;
+}
+
+impl ShouldNotSpawnWithTrait {
+ pub fn should_not_spawn_with_trait() -> impl ShouldNotSpawnTrait<Item = Self> {
+ ShouldNotSpawnWithTrait
+ }
+}
+
+// Same trait name and same type name should not spawn the lint
+#[derive(Default)]
+pub struct Default;
+
+trait TraitSameTypeName {
+ fn should_not_spawn() -> Self;
+}
+impl TraitSameTypeName for ShouldNotSpawn {
+ fn should_not_spawn() -> Self {
+ ShouldNotSpawn
+ }
+}
+
+struct SelfMethodShouldNotSpawn;
+
+impl SelfMethodShouldNotSpawn {
+ fn self_method_should_not_spawn(self) -> Self {
+ SelfMethodShouldNotSpawn
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/self_named_constructors.stderr b/src/tools/clippy/tests/ui/self_named_constructors.stderr
new file mode 100644
index 000000000..ba989f06d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/self_named_constructors.stderr
@@ -0,0 +1,12 @@
+error: constructor `should_spawn` has the same name as the type
+ --> $DIR/self_named_constructors.rs:7:5
+ |
+LL | / pub fn should_spawn() -> ShouldSpawn {
+LL | | ShouldSpawn
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::self-named-constructors` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
new file mode 100644
index 000000000..91916e748
--- /dev/null
+++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
@@ -0,0 +1,122 @@
+#![warn(clippy::semicolon_if_nothing_returned)]
+#![allow(clippy::redundant_closure)]
+#![feature(label_break_value)]
+#![feature(let_else)]
+
+fn get_unit() {}
+
+// the functions below trigger the lint
+fn main() {
+ println!("Hello")
+}
+
+fn hello() {
+ get_unit()
+}
+
+fn basic101(x: i32) {
+ let y: i32;
+ y = x + 1
+}
+
+#[rustfmt::skip]
+fn closure_error() {
+ let _d = || {
+ hello()
+ };
+}
+
+#[rustfmt::skip]
+fn unsafe_checks_error() {
+ use std::mem::MaybeUninit;
+ use std::ptr;
+
+ let mut s = MaybeUninit::<String>::uninit();
+ let _d = || unsafe {
+ ptr::drop_in_place(s.as_mut_ptr())
+ };
+}
+
+// this is fine
+fn print_sum(a: i32, b: i32) {
+ println!("{}", a + b);
+ assert_eq!(true, false);
+}
+
+fn foo(x: i32) {
+ let y: i32;
+ if x < 1 {
+ y = 4;
+ } else {
+ y = 5;
+ }
+}
+
+fn bar(x: i32) {
+ let y: i32;
+ match x {
+ 1 => y = 4,
+ _ => y = 32,
+ }
+}
+
+fn foobar(x: i32) {
+ let y: i32;
+ 'label: {
+ y = x + 1;
+ }
+}
+
+fn loop_test(x: i32) {
+ let y: i32;
+ for &ext in &["stdout", "stderr", "fixed"] {
+ println!("{}", ext);
+ }
+}
+
+fn closure() {
+ let _d = || hello();
+}
+
+#[rustfmt::skip]
+fn closure_block() {
+ let _d = || { hello() };
+}
+
+unsafe fn some_unsafe_op() {}
+unsafe fn some_other_unsafe_fn() {}
+
+fn do_something() {
+ unsafe { some_unsafe_op() };
+
+ unsafe { some_other_unsafe_fn() };
+}
+
+fn unsafe_checks() {
+ use std::mem::MaybeUninit;
+ use std::ptr;
+
+ let mut s = MaybeUninit::<String>::uninit();
+ let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
+}
+
+// Issue #7768
+#[rustfmt::skip]
+fn macro_with_semicolon() {
+ macro_rules! repro {
+ () => {
+ while false {
+ }
+ };
+ }
+ repro!();
+}
+
+fn function_returning_option() -> Option<i32> {
+ Some(1)
+}
+
+// No warning
+fn let_else_stmts() {
+ let Some(x) = function_returning_option() else { return; };
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
new file mode 100644
index 000000000..41d2c1cfb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
@@ -0,0 +1,34 @@
+error: consider adding a `;` to the last statement for consistent formatting
+ --> $DIR/semicolon_if_nothing_returned.rs:10:5
+ |
+LL | println!("Hello")
+ | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
+ |
+ = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
+
+error: consider adding a `;` to the last statement for consistent formatting
+ --> $DIR/semicolon_if_nothing_returned.rs:14:5
+ |
+LL | get_unit()
+ | ^^^^^^^^^^ help: add a `;` here: `get_unit();`
+
+error: consider adding a `;` to the last statement for consistent formatting
+ --> $DIR/semicolon_if_nothing_returned.rs:19:5
+ |
+LL | y = x + 1
+ | ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
+
+error: consider adding a `;` to the last statement for consistent formatting
+ --> $DIR/semicolon_if_nothing_returned.rs:25:9
+ |
+LL | hello()
+ | ^^^^^^^ help: add a `;` here: `hello();`
+
+error: consider adding a `;` to the last statement for consistent formatting
+ --> $DIR/semicolon_if_nothing_returned.rs:36:9
+ |
+LL | ptr::drop_in_place(s.as_mut_ptr())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/serde.rs b/src/tools/clippy/tests/ui/serde.rs
new file mode 100644
index 000000000..5843344eb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/serde.rs
@@ -0,0 +1,47 @@
+#![warn(clippy::serde_api_misuse)]
+#![allow(dead_code)]
+
+extern crate serde;
+
+struct A;
+
+impl<'de> serde::de::Visitor<'de> for A {
+ type Value = ();
+
+ fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ unimplemented!()
+ }
+
+ fn visit_str<E>(self, _v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ unimplemented!()
+ }
+
+ fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ unimplemented!()
+ }
+}
+
+struct B;
+
+impl<'de> serde::de::Visitor<'de> for B {
+ type Value = ();
+
+ fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ unimplemented!()
+ }
+
+ fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ unimplemented!()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/serde.stderr b/src/tools/clippy/tests/ui/serde.stderr
new file mode 100644
index 000000000..760c9c990
--- /dev/null
+++ b/src/tools/clippy/tests/ui/serde.stderr
@@ -0,0 +1,15 @@
+error: you should not implement `visit_string` without also implementing `visit_str`
+ --> $DIR/serde.rs:39:5
+ |
+LL | / fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
+LL | | where
+LL | | E: serde::de::Error,
+LL | | {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::serde-api-misuse` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs
new file mode 100644
index 000000000..1fa9fc749
--- /dev/null
+++ b/src/tools/clippy/tests/ui/shadow.rs
@@ -0,0 +1,98 @@
+#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
+#![allow(clippy::let_unit_value)]
+
+fn shadow_same() {
+ let x = 1;
+ let x = x;
+ let mut x = &x;
+ let x = &mut x;
+ let x = *x;
+}
+
+fn shadow_reuse() -> Option<()> {
+ let x = ([[0]], ());
+ let x = x.0;
+ let x = x[0];
+ let [x] = x;
+ let x = Some(x);
+ let x = foo(x);
+ let x = || x;
+ let x = Some(1).map(|_| x)?;
+ let y = 1;
+ let y = match y {
+ 1 => 2,
+ _ => 3,
+ };
+ None
+}
+
+fn shadow_unrelated() {
+ let x = 1;
+ let x = 2;
+}
+
+fn syntax() {
+ fn f(x: u32) {
+ let x = 1;
+ }
+ let x = 1;
+ match Some(1) {
+ Some(1) => {},
+ Some(x) => {
+ let x = 1;
+ },
+ _ => {},
+ }
+ if let Some(x) = Some(1) {}
+ while let Some(x) = Some(1) {}
+ let _ = |[x]: [u32; 1]| {
+ let x = 1;
+ };
+ let y = Some(1);
+ if let Some(y) = y {}
+}
+
+fn negative() {
+ match Some(1) {
+ Some(x) if x == 1 => {},
+ Some(x) => {},
+ None => {},
+ }
+ match [None, Some(1)] {
+ [Some(x), None] | [None, Some(x)] => {},
+ _ => {},
+ }
+ if let Some(x) = Some(1) {
+ let y = 1;
+ } else {
+ let x = 1;
+ let y = 1;
+ }
+ let x = 1;
+ #[allow(clippy::shadow_unrelated)]
+ let x = 1;
+}
+
+fn foo<T>(_: T) {}
+
+fn question_mark() -> Option<()> {
+ let val = 1;
+ // `?` expands with a `val` binding
+ None?;
+ None
+}
+
+pub async fn foo1(_a: i32) {}
+
+pub async fn foo2(_a: i32, _b: i64) {
+ let _b = _a;
+}
+
+fn ice_8748() {
+ let _ = [0; {
+ let x = 1;
+ if let Some(x) = Some(1) { x } else { 1 }
+ }];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr
new file mode 100644
index 000000000..43d76094d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/shadow.stderr
@@ -0,0 +1,281 @@
+error: `x` is shadowed by itself in `x`
+ --> $DIR/shadow.rs:6:9
+ |
+LL | let x = x;
+ | ^
+ |
+ = note: `-D clippy::shadow-same` implied by `-D warnings`
+note: previous binding is here
+ --> $DIR/shadow.rs:5:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `mut x` is shadowed by itself in `&x`
+ --> $DIR/shadow.rs:7:13
+ |
+LL | let mut x = &x;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:6:9
+ |
+LL | let x = x;
+ | ^
+
+error: `x` is shadowed by itself in `&mut x`
+ --> $DIR/shadow.rs:8:9
+ |
+LL | let x = &mut x;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:7:9
+ |
+LL | let mut x = &x;
+ | ^^^^^
+
+error: `x` is shadowed by itself in `*x`
+ --> $DIR/shadow.rs:9:9
+ |
+LL | let x = *x;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:8:9
+ |
+LL | let x = &mut x;
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:14:9
+ |
+LL | let x = x.0;
+ | ^
+ |
+ = note: `-D clippy::shadow-reuse` implied by `-D warnings`
+note: previous binding is here
+ --> $DIR/shadow.rs:13:9
+ |
+LL | let x = ([[0]], ());
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:15:9
+ |
+LL | let x = x[0];
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:14:9
+ |
+LL | let x = x.0;
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:16:10
+ |
+LL | let [x] = x;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:15:9
+ |
+LL | let x = x[0];
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:17:9
+ |
+LL | let x = Some(x);
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:16:10
+ |
+LL | let [x] = x;
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:18:9
+ |
+LL | let x = foo(x);
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:17:9
+ |
+LL | let x = Some(x);
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:19:9
+ |
+LL | let x = || x;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:18:9
+ |
+LL | let x = foo(x);
+ | ^
+
+error: `x` is shadowed
+ --> $DIR/shadow.rs:20:9
+ |
+LL | let x = Some(1).map(|_| x)?;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:19:9
+ |
+LL | let x = || x;
+ | ^
+
+error: `y` is shadowed
+ --> $DIR/shadow.rs:22:9
+ |
+LL | let y = match y {
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:21:9
+ |
+LL | let y = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:31:9
+ |
+LL | let x = 2;
+ | ^
+ |
+ = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
+note: previous binding is here
+ --> $DIR/shadow.rs:30:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:36:13
+ |
+LL | let x = 1;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:35:10
+ |
+LL | fn f(x: u32) {
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:41:14
+ |
+LL | Some(x) => {
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:38:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:42:17
+ |
+LL | let x = 1;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:41:14
+ |
+LL | Some(x) => {
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:46:17
+ |
+LL | if let Some(x) = Some(1) {}
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:38:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:47:20
+ |
+LL | while let Some(x) = Some(1) {}
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:38:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:48:15
+ |
+LL | let _ = |[x]: [u32; 1]| {
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:38:9
+ |
+LL | let x = 1;
+ | ^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:49:13
+ |
+LL | let x = 1;
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:48:15
+ |
+LL | let _ = |[x]: [u32; 1]| {
+ | ^
+
+error: `y` is shadowed
+ --> $DIR/shadow.rs:52:17
+ |
+LL | if let Some(y) = y {}
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:51:9
+ |
+LL | let y = Some(1);
+ | ^
+
+error: `_b` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:88:9
+ |
+LL | let _b = _a;
+ | ^^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:87:28
+ |
+LL | pub async fn foo2(_a: i32, _b: i64) {
+ | ^^
+
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:94:21
+ |
+LL | if let Some(x) = Some(1) { x } else { 1 }
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:93:13
+ |
+LL | let x = 1;
+ | ^
+
+error: aborting due to 23 previous errors
+
diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.fixed b/src/tools/clippy/tests/ui/short_circuit_statement.fixed
new file mode 100644
index 000000000..dd22ecab0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/short_circuit_statement.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::short_circuit_statement)]
+#![allow(clippy::nonminimal_bool)]
+
+fn main() {
+ if f() { g(); }
+ if !f() { g(); }
+ if 1 != 2 { g(); }
+}
+
+fn f() -> bool {
+ true
+}
+
+fn g() -> bool {
+ false
+}
diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.rs b/src/tools/clippy/tests/ui/short_circuit_statement.rs
new file mode 100644
index 000000000..73a55bf1f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/short_circuit_statement.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::short_circuit_statement)]
+#![allow(clippy::nonminimal_bool)]
+
+fn main() {
+ f() && g();
+ f() || g();
+ 1 == 2 || g();
+}
+
+fn f() -> bool {
+ true
+}
+
+fn g() -> bool {
+ false
+}
diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.stderr b/src/tools/clippy/tests/ui/short_circuit_statement.stderr
new file mode 100644
index 000000000..aa84ac3a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/short_circuit_statement.stderr
@@ -0,0 +1,22 @@
+error: boolean short circuit operator in statement may be clearer using an explicit test
+ --> $DIR/short_circuit_statement.rs:7:5
+ |
+LL | f() && g();
+ | ^^^^^^^^^^^ help: replace it with: `if f() { g(); }`
+ |
+ = note: `-D clippy::short-circuit-statement` implied by `-D warnings`
+
+error: boolean short circuit operator in statement may be clearer using an explicit test
+ --> $DIR/short_circuit_statement.rs:8:5
+ |
+LL | f() || g();
+ | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }`
+
+error: boolean short circuit operator in statement may be clearer using an explicit test
+ --> $DIR/short_circuit_statement.rs:9:5
+ |
+LL | 1 == 2 || g();
+ | ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
new file mode 100644
index 000000000..50999c6f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
@@ -0,0 +1,84 @@
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc,
+ clippy::return_self_not_must_use,
+ clippy::unused_async
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+pub struct T1;
+impl T1 {
+ // corner cases: should not lint
+
+ // no error, not public interface
+ pub(crate) fn drop(&mut self) {}
+
+ // no error, private function
+ fn neg(self) -> Self {
+ self
+ }
+
+ // no error, private function
+ fn eq(&self, other: Self) -> bool {
+ true
+ }
+
+ // No error; self is a ref.
+ fn sub(&self, other: Self) -> &Self {
+ self
+ }
+
+ // No error; different number of arguments.
+ fn div(self) -> Self {
+ self
+ }
+
+ // No error; wrong return type.
+ fn rem(self, other: Self) {}
+
+ // Fine
+ fn into_u32(self) -> u32 {
+ 0
+ }
+
+ fn into_u16(&self) -> u16 {
+ 0
+ }
+
+ fn to_something(self) -> u32 {
+ 0
+ }
+
+ fn new(self) -> Self {
+ unimplemented!();
+ }
+
+ pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> {
+ unimplemented!();
+ }
+}
+
+pub struct T2;
+impl T2 {
+ // Shouldn't trigger lint as it is unsafe.
+ pub unsafe fn add(self, rhs: Self) -> Self {
+ self
+ }
+
+ // Should not trigger lint since this is an async function.
+ pub async fn next(&mut self) -> Option<Self> {
+ None
+ }
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs
new file mode 100644
index 000000000..20d49f5a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs
@@ -0,0 +1,87 @@
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc,
+ clippy::return_self_not_must_use
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+ // *****************************************
+ // trait method list part 1, should lint all
+ // *****************************************
+ pub fn add(self, other: T) -> T {
+ unimplemented!()
+ }
+
+ pub fn as_mut(&mut self) -> &mut T {
+ unimplemented!()
+ }
+
+ pub fn as_ref(&self) -> &T {
+ unimplemented!()
+ }
+
+ pub fn bitand(self, rhs: T) -> T {
+ unimplemented!()
+ }
+
+ pub fn bitor(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn bitxor(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn borrow(&self) -> &str {
+ unimplemented!()
+ }
+
+ pub fn borrow_mut(&mut self) -> &mut str {
+ unimplemented!()
+ }
+
+ pub fn clone(&self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn cmp(&self, other: &Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn default() -> Self {
+ unimplemented!()
+ }
+
+ pub fn deref(&self) -> &Self {
+ unimplemented!()
+ }
+
+ pub fn deref_mut(&mut self) -> &mut Self {
+ unimplemented!()
+ }
+
+ pub fn div(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn drop(&mut self) {
+ unimplemented!()
+ }
+ // **********
+ // part 1 end
+ // **********
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr
new file mode 100644
index 000000000..2b7d4628c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr
@@ -0,0 +1,143 @@
+error: method `add` can be confused for the standard trait method `std::ops::Add::add`
+ --> $DIR/method_list_1.rs:25:5
+ |
+LL | / pub fn add(self, other: T) -> T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+ = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name
+
+error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut`
+ --> $DIR/method_list_1.rs:29:5
+ |
+LL | / pub fn as_mut(&mut self) -> &mut T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name
+
+error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref`
+ --> $DIR/method_list_1.rs:33:5
+ |
+LL | / pub fn as_ref(&self) -> &T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name
+
+error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand`
+ --> $DIR/method_list_1.rs:37:5
+ |
+LL | / pub fn bitand(self, rhs: T) -> T {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name
+
+error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor`
+ --> $DIR/method_list_1.rs:41:5
+ |
+LL | / pub fn bitor(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name
+
+error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor`
+ --> $DIR/method_list_1.rs:45:5
+ |
+LL | / pub fn bitxor(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name
+
+error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow`
+ --> $DIR/method_list_1.rs:49:5
+ |
+LL | / pub fn borrow(&self) -> &str {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name
+
+error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut`
+ --> $DIR/method_list_1.rs:53:5
+ |
+LL | / pub fn borrow_mut(&mut self) -> &mut str {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name
+
+error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone`
+ --> $DIR/method_list_1.rs:57:5
+ |
+LL | / pub fn clone(&self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name
+
+error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp`
+ --> $DIR/method_list_1.rs:61:5
+ |
+LL | / pub fn cmp(&self, other: &Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name
+
+error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref`
+ --> $DIR/method_list_1.rs:69:5
+ |
+LL | / pub fn deref(&self) -> &Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name
+
+error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut`
+ --> $DIR/method_list_1.rs:73:5
+ |
+LL | / pub fn deref_mut(&mut self) -> &mut Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name
+
+error: method `div` can be confused for the standard trait method `std::ops::Div::div`
+ --> $DIR/method_list_1.rs:77:5
+ |
+LL | / pub fn div(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name
+
+error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop`
+ --> $DIR/method_list_1.rs:81:5
+ |
+LL | / pub fn drop(&mut self) {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs
new file mode 100644
index 000000000..3efec1c52
--- /dev/null
+++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs
@@ -0,0 +1,88 @@
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::needless_pass_by_value,
+ clippy::must_use_candidate,
+ clippy::unused_self,
+ clippy::needless_lifetimes,
+ clippy::missing_safety_doc,
+ clippy::wrong_self_convention,
+ clippy::missing_panics_doc,
+ clippy::return_self_not_must_use
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+ // *****************************************
+ // trait method list part 2, should lint all
+ // *****************************************
+
+ pub fn eq(&self, other: &Self) -> bool {
+ unimplemented!()
+ }
+
+ pub fn from_iter<T>(iter: T) -> Self {
+ unimplemented!()
+ }
+
+ pub fn from_str(s: &str) -> Result<Self, Self> {
+ unimplemented!()
+ }
+
+ pub fn hash(&self, state: &mut T) {
+ unimplemented!()
+ }
+
+ pub fn index(&self, index: usize) -> &Self {
+ unimplemented!()
+ }
+
+ pub fn index_mut(&mut self, index: usize) -> &mut Self {
+ unimplemented!()
+ }
+
+ pub fn into_iter(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn mul(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn neg(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn next(&mut self) -> Option<Self> {
+ unimplemented!()
+ }
+
+ pub fn not(self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn rem(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn shl(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn shr(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+
+ pub fn sub(self, rhs: Self) -> Self {
+ unimplemented!()
+ }
+ // **********
+ // part 2 end
+ // **********
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr
new file mode 100644
index 000000000..b6fd43569
--- /dev/null
+++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr
@@ -0,0 +1,153 @@
+error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq`
+ --> $DIR/method_list_2.rs:26:5
+ |
+LL | / pub fn eq(&self, other: &Self) -> bool {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+ = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name
+
+error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter`
+ --> $DIR/method_list_2.rs:30:5
+ |
+LL | / pub fn from_iter<T>(iter: T) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name
+
+error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str`
+ --> $DIR/method_list_2.rs:34:5
+ |
+LL | / pub fn from_str(s: &str) -> Result<Self, Self> {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name
+
+error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash`
+ --> $DIR/method_list_2.rs:38:5
+ |
+LL | / pub fn hash(&self, state: &mut T) {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name
+
+error: method `index` can be confused for the standard trait method `std::ops::Index::index`
+ --> $DIR/method_list_2.rs:42:5
+ |
+LL | / pub fn index(&self, index: usize) -> &Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name
+
+error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut`
+ --> $DIR/method_list_2.rs:46:5
+ |
+LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name
+
+error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`
+ --> $DIR/method_list_2.rs:50:5
+ |
+LL | / pub fn into_iter(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name
+
+error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul`
+ --> $DIR/method_list_2.rs:54:5
+ |
+LL | / pub fn mul(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name
+
+error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg`
+ --> $DIR/method_list_2.rs:58:5
+ |
+LL | / pub fn neg(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name
+
+error: method `next` can be confused for the standard trait method `std::iter::Iterator::next`
+ --> $DIR/method_list_2.rs:62:5
+ |
+LL | / pub fn next(&mut self) -> Option<Self> {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name
+
+error: method `not` can be confused for the standard trait method `std::ops::Not::not`
+ --> $DIR/method_list_2.rs:66:5
+ |
+LL | / pub fn not(self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name
+
+error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem`
+ --> $DIR/method_list_2.rs:70:5
+ |
+LL | / pub fn rem(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name
+
+error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl`
+ --> $DIR/method_list_2.rs:74:5
+ |
+LL | / pub fn shl(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name
+
+error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr`
+ --> $DIR/method_list_2.rs:78:5
+ |
+LL | / pub fn shr(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name
+
+error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub`
+ --> $DIR/method_list_2.rs:82:5
+ |
+LL | / pub fn sub(self, rhs: Self) -> Self {
+LL | | unimplemented!()
+LL | | }
+ | |_____^
+ |
+ = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
new file mode 100644
index 000000000..84ecf1ea5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
@@ -0,0 +1,630 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+// // run-rustfix
+
+#![warn(clippy::significant_drop_in_scrutinee)]
+#![allow(clippy::single_match)]
+#![allow(clippy::match_single_binding)]
+#![allow(unused_assignments)]
+#![allow(dead_code)]
+
+use std::num::ParseIntError;
+use std::ops::Deref;
+use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::RwLock;
+use std::sync::{Mutex, MutexGuard};
+
+struct State {}
+
+impl State {
+ fn foo(&self) -> bool {
+ true
+ }
+
+ fn bar(&self) {}
+}
+
+fn should_not_trigger_lint_with_mutex_guard_outside_match() {
+ let mutex = Mutex::new(State {});
+
+ // Should not trigger lint because the temporary should drop at the `;` on line before the match
+ let is_foo = mutex.lock().unwrap().foo();
+ match is_foo {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
+ let mutex = Mutex::new(State {});
+
+ // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
+ // so its lifetime should not be surprising.
+ match mutex.lock() {
+ Ok(guard) => {
+ guard.foo();
+ mutex.lock().unwrap().bar();
+ },
+ _ => {},
+ };
+}
+
+fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
+ let mutex = Mutex::new(State {});
+
+ // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
+ // is preserved until the end of the match, but there is no clear indication that this is the
+ // case.
+ match mutex.lock().unwrap().foo() {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_with_mutex_guard_in_match_scrutinee_when_lint_allowed() {
+ let mutex = Mutex::new(State {});
+
+ // Lint should not be triggered because it is "allowed" below.
+ #[allow(clippy::significant_drop_in_scrutinee)]
+ match mutex.lock().unwrap().foo() {
+ true => {
+ mutex.lock().unwrap().bar();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_insignificant_drop() {
+ // Should not trigger lint because there are no temporaries whose drops have a significant
+ // side effect.
+ match 1u64.to_string().is_empty() {
+ true => {
+ println!("It was empty")
+ },
+ false => {
+ println!("It was not empty")
+ },
+ }
+}
+
+struct StateWithMutex {
+ m: Mutex<u64>,
+}
+
+struct MutexGuardWrapper<'a> {
+ mg: MutexGuard<'a, u64>,
+}
+
+impl<'a> MutexGuardWrapper<'a> {
+ fn get_the_value(&self) -> u64 {
+ *self.mg.deref()
+ }
+}
+
+struct MutexGuardWrapperWrapper<'a> {
+ mg: MutexGuardWrapper<'a>,
+}
+
+impl<'a> MutexGuardWrapperWrapper<'a> {
+ fn get_the_value(&self) -> u64 {
+ *self.mg.mg.deref()
+ }
+}
+
+impl StateWithMutex {
+ fn lock_m(&self) -> MutexGuardWrapper<'_> {
+ MutexGuardWrapper {
+ mg: self.m.lock().unwrap(),
+ }
+ }
+
+ fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
+ MutexGuardWrapperWrapper {
+ mg: MutexGuardWrapper {
+ mg: self.m.lock().unwrap(),
+ },
+ }
+ }
+
+ fn foo(&self) -> bool {
+ true
+ }
+
+ fn bar(&self) {}
+}
+
+fn should_trigger_lint_with_wrapped_mutex() {
+ let s = StateWithMutex { m: Mutex::new(1) };
+
+ // Should trigger lint because a temporary contains a type with a significant drop and its
+ // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
+ // the temporary contains such a type, making it potentially even more surprising.
+ match s.lock_m().get_the_value() {
+ 1 => {
+ println!("Got 1. Is it still 1?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ 2 => {
+ println!("Got 2. Is it still 2?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ _ => {},
+ }
+ println!("All done!");
+}
+
+fn should_trigger_lint_with_double_wrapped_mutex() {
+ let s = StateWithMutex { m: Mutex::new(1) };
+
+ // Should trigger lint because a temporary contains a type which further contains a type with a
+ // significant drop and its lifetime is not obvious. Additionally, it is not obvious from
+ // looking at the scrutinee that the temporary contains such a type, making it potentially even
+ // more surprising.
+ match s.lock_m_m().get_the_value() {
+ 1 => {
+ println!("Got 1. Is it still 1?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ 2 => {
+ println!("Got 2. Is it still 2?");
+ println!("{}", s.lock_m().get_the_value());
+ },
+ _ => {},
+ }
+ println!("All done!");
+}
+
+struct Counter {
+ i: AtomicU64,
+}
+
+#[clippy::has_significant_drop]
+struct CounterWrapper<'a> {
+ counter: &'a Counter,
+}
+
+impl<'a> CounterWrapper<'a> {
+ fn new(counter: &Counter) -> CounterWrapper {
+ counter.i.fetch_add(1, Ordering::Relaxed);
+ CounterWrapper { counter }
+ }
+}
+
+impl<'a> Drop for CounterWrapper<'a> {
+ fn drop(&mut self) {
+ self.counter.i.fetch_sub(1, Ordering::Relaxed);
+ }
+}
+
+impl Counter {
+ fn temp_increment(&self) -> Vec<CounterWrapper> {
+ vec![CounterWrapper::new(self), CounterWrapper::new(self)]
+ }
+}
+
+fn should_trigger_lint_for_vec() {
+ let counter = Counter { i: AtomicU64::new(0) };
+
+ // Should trigger lint because the temporary in the scrutinee returns a collection of types
+ // which have significant drops. The types with significant drops are also non-obvious when
+ // reading the expression in the scrutinee.
+ match counter.temp_increment().len() {
+ 2 => {
+ let current_count = counter.i.load(Ordering::Relaxed);
+ println!("Current count {}", current_count);
+ assert_eq!(current_count, 0);
+ },
+ 1 => {},
+ 3 => {},
+ _ => {},
+ };
+}
+
+struct StateWithField {
+ s: String,
+}
+
+// Should trigger lint only on the type in the tuple which is created using a temporary
+// with a significant drop. Additionally, this test ensures that the format of the tuple
+// is preserved correctly in the suggestion.
+fn should_trigger_lint_for_tuple_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ {
+ match (mutex1.lock().unwrap().s.len(), true) {
+ (3, _) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _) => {},
+ };
+
+ match (true, mutex1.lock().unwrap().s.len(), true) {
+ (_, 3, _) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _, _) => {},
+ };
+
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+ match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
+ (3, _, 3) => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _, _) => {},
+ };
+
+ let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
+ match mutex3.lock().unwrap().s.as_str() {
+ "three" => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ _ => {},
+ };
+
+ match (true, mutex3.lock().unwrap().s.as_str()) {
+ (_, "three") => {
+ println!("started");
+ mutex1.lock().unwrap().s.len();
+ mutex2.lock().unwrap().s.len();
+ println!("done");
+ },
+ (_, _) => {},
+ };
+ }
+}
+
+// Should trigger lint when either side of a binary operation creates a temporary with a
+// significant drop.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
+ let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
+
+ match mutex.lock().unwrap().s.len() > 1 {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+
+ match 1 < mutex.lock().unwrap().s.len() {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+// Should trigger lint when both sides of a binary operation creates a temporary with a
+// significant drop.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
+ let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
+ let mutex2 = Mutex::new(StateWithField {
+ s: "statewithfield".to_owned(),
+ });
+
+ match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
+ true => {
+ println!(
+ "{} < {}",
+ mutex1.lock().unwrap().s.len(),
+ mutex2.lock().unwrap().s.len()
+ );
+ },
+ false => {},
+ };
+
+ match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
+ true => {
+ println!(
+ "{} >= {}",
+ mutex1.lock().unwrap().s.len(),
+ mutex2.lock().unwrap().s.len()
+ );
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_closure_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ let get_mutex_guard = || mutex1.lock().unwrap().s.len();
+
+ // Should not trigger lint because the temporary with a significant drop will be dropped
+ // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
+ // surprising lifetime.
+ match get_mutex_guard() > 1 {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+
+ let get_mutex_guard = || mutex1.lock().unwrap();
+
+ // Should trigger lint because the temporary with a significant drop is returned from the
+ // closure but not used directly in any match arms, so it has a potentially surprising lifetime.
+ match get_mutex_guard().s.len() > 1 {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_trigger_lint_for_return_from_match_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+
+ let i = 100;
+
+ // Should trigger lint because the nested match within the scrutinee returns a temporary with a
+ // significant drop is but not used directly in any match arms, so it has a potentially
+ // surprising lifetime.
+ match match i {
+ 100 => mutex1.lock().unwrap(),
+ _ => mutex2.lock().unwrap(),
+ }
+ .s
+ .len()
+ > 1
+ {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {
+ println!("nothing to do here");
+ },
+ };
+}
+
+fn should_trigger_lint_for_return_from_if_in_scrutinee() {
+ let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
+ let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
+
+ let i = 100;
+
+ // Should trigger lint because the nested if-expression within the scrutinee returns a temporary
+ // with a significant drop is but not used directly in any match arms, so it has a potentially
+ // surprising lifetime.
+ match if i > 1 {
+ mutex1.lock().unwrap()
+ } else {
+ mutex2.lock().unwrap()
+ }
+ .s
+ .len()
+ > 1
+ {
+ true => {
+ mutex1.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+fn should_not_trigger_lint_for_if_in_scrutinee() {
+ let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
+
+ let i = 100;
+
+ // Should not trigger the lint because the temporary with a significant drop *is* dropped within
+ // the body of the if-expression nested within the match scrutinee, and therefore does not have
+ // a potentially surprising lifetime.
+ match if i > 1 {
+ mutex.lock().unwrap().s.len() > 1
+ } else {
+ false
+ } {
+ true => {
+ mutex.lock().unwrap().s.len();
+ },
+ false => {},
+ };
+}
+
+struct StateWithBoxedMutexGuard {
+ u: Mutex<u64>,
+}
+
+impl StateWithBoxedMutexGuard {
+ fn new() -> StateWithBoxedMutexGuard {
+ StateWithBoxedMutexGuard { u: Mutex::new(42) }
+ }
+ fn lock(&self) -> Box<MutexGuard<u64>> {
+ Box::new(self.u.lock().unwrap())
+ }
+}
+
+fn should_trigger_lint_for_boxed_mutex_guard() {
+ let s = StateWithBoxedMutexGuard::new();
+
+ // Should trigger lint because a temporary Box holding a type with a significant drop in a match
+ // scrutinee may have a potentially surprising lifetime.
+ match s.lock().deref().deref() {
+ 0 | 1 => println!("Value was less than 2"),
+ _ => println!("Value is {}", s.lock().deref()),
+ };
+}
+
+struct StateStringWithBoxedMutexGuard {
+ s: Mutex<String>,
+}
+
+impl StateStringWithBoxedMutexGuard {
+ fn new() -> StateStringWithBoxedMutexGuard {
+ StateStringWithBoxedMutexGuard {
+ s: Mutex::new("A String".to_owned()),
+ }
+ }
+ fn lock(&self) -> Box<MutexGuard<String>> {
+ Box::new(self.s.lock().unwrap())
+ }
+}
+
+fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
+ let s = StateStringWithBoxedMutexGuard::new();
+
+ let matcher = String::from("A String");
+
+ // Should trigger lint because a temporary Box holding a type with a significant drop in a match
+ // scrutinee may have a potentially surprising lifetime.
+ match s.lock().deref().deref() {
+ matcher => println!("Value is {}", s.lock().deref()),
+ _ => println!("Value was not a match"),
+ };
+}
+
+struct StateWithIntField {
+ i: u64,
+}
+
+// Should trigger lint when either side of an assign expression contains a temporary with a
+// significant drop, because the temporary's lifetime will be extended to the end of the match.
+// To avoid potential unnecessary copies or creating references that would trigger the significant
+// drop problem, the lint recommends moving the entire binary operation.
+fn should_trigger_lint_in_assign_expr() {
+ let mutex = Mutex::new(StateWithIntField { i: 10 });
+
+ let mut i = 100;
+
+ match mutex.lock().unwrap().i = i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match i = mutex.lock().unwrap().i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match mutex.lock().unwrap().i += 1 {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+
+ match i += mutex.lock().unwrap().i {
+ _ => {
+ println!("{}", mutex.lock().unwrap().i);
+ },
+ };
+}
+
+#[derive(Debug)]
+enum RecursiveEnum {
+ Foo(Option<Box<RecursiveEnum>>),
+}
+
+#[derive(Debug)]
+enum GenericRecursiveEnum<T> {
+ Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
+}
+
+fn should_not_cause_stack_overflow() {
+ // Test that when a type recursively contains itself, a stack overflow does not occur when
+ // checking sub-types for significant drops.
+ let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
+ match f {
+ RecursiveEnum::Foo(Some(f)) => {
+ println!("{:?}", f)
+ },
+ RecursiveEnum::Foo(f) => {
+ println!("{:?}", f)
+ },
+ }
+
+ let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
+ match f {
+ GenericRecursiveEnum::Foo(i, Some(f)) => {
+ println!("{} {:?}", i, f)
+ },
+ GenericRecursiveEnum::Foo(i, f) => {
+ println!("{} {:?}", i, f)
+ },
+ }
+}
+
+fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
+ // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
+ // for this lint
+ let rwlock = RwLock::new("1".to_string());
+ let result = rwlock.read().unwrap().parse::<u64>()?;
+ println!("{}", result);
+ rwlock.write().unwrap().push('2');
+ Ok(result)
+}
+
+struct ResultReturner {
+ s: String,
+}
+
+impl ResultReturner {
+ fn to_number(&self) -> Result<i64, ParseIntError> {
+ self.s.parse::<i64>()
+ }
+}
+
+fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
+ let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
+ match rwlock.read().unwrap().to_number() {
+ Ok(n) => println!("Converted to number: {}", n),
+ Err(e) => println!("Could not convert {} to number", e),
+ };
+}
+
+fn should_trigger_lint_for_read_write_lock_for_loop() {
+ // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
+ // designed to look for.
+ let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
+ for s in rwlock.read().unwrap().iter() {
+ println!("{}", s);
+ }
+}
+
+fn do_bar(mutex: &Mutex<State>) {
+ mutex.lock().unwrap().bar();
+}
+
+fn should_trigger_lint_without_significant_drop_in_arm() {
+ let mutex = Mutex::new(State {});
+
+ // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
+ // is preserved until the end of the match, but there is no clear indication that this is the
+ // case.
+ match mutex.lock().unwrap().foo() {
+ true => do_bar(&mutex),
+ false => {},
+ };
+}
+
+fn should_not_trigger_on_significant_iterator_drop() {
+ let lines = std::io::stdin().lines();
+ for line in lines {
+ println!("foo: {}", line.unwrap());
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
new file mode 100644
index 000000000..88ea6bce2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
@@ -0,0 +1,497 @@
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:59:11
+ |
+LL | match mutex.lock().unwrap().foo() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | true => {
+LL | mutex.lock().unwrap().bar();
+ | --------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings`
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex.lock().unwrap().foo();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:145:11
+ |
+LL | match s.lock_m().get_the_value() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | println!("{}", s.lock_m().get_the_value());
+ | ---------- another value with significant `Drop` created here
+...
+LL | }
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = s.lock_m().get_the_value();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:166:11
+ |
+LL | match s.lock_m_m().get_the_value() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | println!("{}", s.lock_m().get_the_value());
+ | ---------- another value with significant `Drop` created here
+...
+LL | }
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = s.lock_m_m().get_the_value();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:214:11
+ |
+LL | match counter.temp_increment().len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = counter.temp_increment().len();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:237:16
+ |
+LL | match (mutex1.lock().unwrap().s.len(), true) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex1.lock().unwrap().s.len();
+LL ~ match (value, true) {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:246:22
+ |
+LL | match (true, mutex1.lock().unwrap().s.len(), true) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex1.lock().unwrap().s.len();
+LL ~ match (true, value, true) {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:256:16
+ |
+LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex1.lock().unwrap().s.len();
+LL ~ match (value, true, mutex2.lock().unwrap().s.len()) {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:256:54
+ |
+LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex2.lock().unwrap().s.len();
+LL ~ match (mutex1.lock().unwrap().s.len(), true, value) {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:267:15
+ |
+LL | match mutex3.lock().unwrap().s.as_str() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:277:22
+ |
+LL | match (true, mutex3.lock().unwrap().s.as_str()) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:296:11
+ |
+LL | match mutex.lock().unwrap().s.len() > 1 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | true => {
+LL | mutex.lock().unwrap().s.len();
+ | --------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex.lock().unwrap().s.len() > 1;
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:303:11
+ |
+LL | match 1 < mutex.lock().unwrap().s.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | true => {
+LL | mutex.lock().unwrap().s.len();
+ | --------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = 1 < mutex.lock().unwrap().s.len();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:321:11
+ |
+LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len(),
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len()
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:332:11
+ |
+LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | mutex1.lock().unwrap().s.len(),
+ | ---------------------- another value with significant `Drop` created here
+LL | mutex2.lock().unwrap().s.len()
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:367:11
+ |
+LL | match get_mutex_guard().s.len() > 1 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | true => {
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = get_mutex_guard().s.len() > 1;
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:384:11
+ |
+LL | match match i {
+ | ___________^
+LL | | 100 => mutex1.lock().unwrap(),
+LL | | _ => mutex2.lock().unwrap(),
+LL | | }
+LL | | .s
+LL | | .len()
+LL | | > 1
+ | |___________^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = match i {
+LL + 100 => mutex1.lock().unwrap(),
+LL + _ => mutex2.lock().unwrap(),
+LL + }
+LL + .s
+LL + .len()
+LL + > 1;
+LL ~ match value
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:410:11
+ |
+LL | match if i > 1 {
+ | ___________^
+LL | | mutex1.lock().unwrap()
+LL | | } else {
+LL | | mutex2.lock().unwrap()
+... |
+LL | | .len()
+LL | | > 1
+ | |___________^
+...
+LL | mutex1.lock().unwrap().s.len();
+ | ---------------------- another value with significant `Drop` created here
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = if i > 1 {
+LL + mutex1.lock().unwrap()
+LL + } else {
+LL + mutex2.lock().unwrap()
+LL + }
+LL + .s
+LL + .len()
+LL + > 1;
+LL ~ match value
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:464:11
+ |
+LL | match s.lock().deref().deref() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | 0 | 1 => println!("Value was less than 2"),
+LL | _ => println!("Value is {}", s.lock().deref()),
+ | ---------------- another value with significant `Drop` created here
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match and create a copy
+ |
+LL ~ let value = *s.lock().deref().deref();
+LL ~ match value {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:492:11
+ |
+LL | match s.lock().deref().deref() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | matcher => println!("Value is {}", s.lock().deref()),
+ | ---------------- another value with significant `Drop` created here
+LL | _ => println!("Value was not a match"),
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:511:11
+ |
+LL | match mutex.lock().unwrap().i = i {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | _ => {
+LL | println!("{}", mutex.lock().unwrap().i);
+ | --------------------- another value with significant `Drop` created here
+LL | },
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ mutex.lock().unwrap().i = i;
+LL ~ match () {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:517:11
+ |
+LL | match i = mutex.lock().unwrap().i {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | _ => {
+LL | println!("{}", mutex.lock().unwrap().i);
+ | --------------------- another value with significant `Drop` created here
+LL | },
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ i = mutex.lock().unwrap().i;
+LL ~ match () {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:523:11
+ |
+LL | match mutex.lock().unwrap().i += 1 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | _ => {
+LL | println!("{}", mutex.lock().unwrap().i);
+ | --------------------- another value with significant `Drop` created here
+LL | },
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ mutex.lock().unwrap().i += 1;
+LL ~ match () {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:529:11
+ |
+LL | match i += mutex.lock().unwrap().i {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | _ => {
+LL | println!("{}", mutex.lock().unwrap().i);
+ | --------------------- another value with significant `Drop` created here
+LL | },
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ i += mutex.lock().unwrap().i;
+LL ~ match () {
+ |
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:592:11
+ |
+LL | match rwlock.read().unwrap().to_number() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:602:14
+ |
+LL | for s in rwlock.read().unwrap().iter() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | println!("{}", s);
+LL | }
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+ --> $DIR/significant_drop_in_scrutinee.rs:617:11
+ |
+LL | match mutex.lock().unwrap().foo() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | };
+ | - temporary lives until here
+ |
+ = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+ |
+LL ~ let value = mutex.lock().unwrap().foo();
+LL ~ match value {
+ |
+
+error: aborting due to 26 previous errors
+
diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs
new file mode 100644
index 000000000..c21225d15
--- /dev/null
+++ b/src/tools/clippy/tests/ui/similar_names.rs
@@ -0,0 +1,121 @@
+#![warn(clippy::similar_names)]
+#![allow(
+ unused,
+ clippy::println_empty_string,
+ clippy::empty_loop,
+ clippy::diverging_sub_expression,
+ clippy::let_unit_value
+)]
+
+struct Foo {
+ apple: i32,
+ bpple: i32,
+}
+
+fn main() {
+ let specter: i32;
+ let spectre: i32;
+
+ let apple: i32;
+
+ let bpple: i32;
+
+ let cpple: i32;
+
+ let a_bar: i32;
+ let b_bar: i32;
+ let c_bar: i32;
+
+ let items = [5];
+ for item in &items {
+ loop {}
+ }
+
+ let foo_x: i32;
+ let foo_y: i32;
+
+ let rhs: i32;
+ let lhs: i32;
+
+ let bla_rhs: i32;
+ let bla_lhs: i32;
+
+ let blubrhs: i32;
+ let blublhs: i32;
+
+ let blubx: i32;
+ let bluby: i32;
+
+ let cake: i32;
+ let cakes: i32;
+ let coke: i32;
+
+ match 5 {
+ cheese @ 1 => {},
+ rabbit => panic!(),
+ }
+ let cheese: i32;
+ match (42, 43) {
+ (cheese1, 1) => {},
+ (cheese2, 2) => panic!(),
+ _ => println!(""),
+ }
+ let ipv4: i32;
+ let ipv6: i32;
+ let abcd1: i32;
+ let abdc2: i32;
+ let xyz1abc: i32;
+ let xyz2abc: i32;
+ let xyzeabc: i32;
+
+ let parser: i32;
+ let parsed: i32;
+ let parsee: i32;
+
+ let setter: i32;
+ let getter: i32;
+ let tx1: i32;
+ let rx1: i32;
+ let tx_cake: i32;
+ let rx_cake: i32;
+
+ // names often used in win32 code (for example WindowProc)
+ let wparam: i32;
+ let lparam: i32;
+
+ let iter: i32;
+ let item: i32;
+}
+
+fn foo() {
+ let Foo { apple, bpple } = unimplemented!();
+ let Foo {
+ apple: spring,
+ bpple: sprang,
+ } = unimplemented!();
+}
+
+// false positive similar_names (#3057, #2651)
+// clippy claimed total_reg_src_size and total_size and
+// numb_reg_src_checkouts and total_bin_size were similar
+#[derive(Debug, Clone)]
+pub(crate) struct DirSizes {
+ pub(crate) total_size: u64,
+ pub(crate) numb_bins: u64,
+ pub(crate) total_bin_size: u64,
+ pub(crate) total_reg_size: u64,
+ pub(crate) total_git_db_size: u64,
+ pub(crate) total_git_repos_bare_size: u64,
+ pub(crate) numb_git_repos_bare_repos: u64,
+ pub(crate) numb_git_checkouts: u64,
+ pub(crate) total_git_chk_size: u64,
+ pub(crate) total_reg_cache_size: u64,
+ pub(crate) total_reg_src_size: u64,
+ pub(crate) numb_reg_cache_entries: u64,
+ pub(crate) numb_reg_src_checkouts: u64,
+}
+
+fn ignore_underscore_prefix() {
+ let hello: ();
+ let _hello: ();
+}
diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr
new file mode 100644
index 000000000..6e7726938
--- /dev/null
+++ b/src/tools/clippy/tests/ui/similar_names.stderr
@@ -0,0 +1,87 @@
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:21:9
+ |
+LL | let bpple: i32;
+ | ^^^^^
+ |
+ = note: `-D clippy::similar-names` implied by `-D warnings`
+note: existing binding defined here
+ --> $DIR/similar_names.rs:19:9
+ |
+LL | let apple: i32;
+ | ^^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:23:9
+ |
+LL | let cpple: i32;
+ | ^^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:19:9
+ |
+LL | let apple: i32;
+ | ^^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:47:9
+ |
+LL | let bluby: i32;
+ | ^^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:46:9
+ |
+LL | let blubx: i32;
+ | ^^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:51:9
+ |
+LL | let coke: i32;
+ | ^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:49:9
+ |
+LL | let cake: i32;
+ | ^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:69:9
+ |
+LL | let xyzeabc: i32;
+ | ^^^^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:67:9
+ |
+LL | let xyz1abc: i32;
+ | ^^^^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:73:9
+ |
+LL | let parsee: i32;
+ | ^^^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:71:9
+ |
+LL | let parser: i32;
+ | ^^^^^^
+
+error: binding's name is too similar to existing binding
+ --> $DIR/similar_names.rs:94:16
+ |
+LL | bpple: sprang,
+ | ^^^^^^
+ |
+note: existing binding defined here
+ --> $DIR/similar_names.rs:93:16
+ |
+LL | apple: spring,
+ | ^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.fixed b/src/tools/clippy/tests/ui/single_char_add_str.fixed
new file mode 100644
index 000000000..63a6d37a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_add_str.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+#![warn(clippy::single_char_add_str)]
+
+macro_rules! get_string {
+ () => {
+ String::from("Hello world!")
+ };
+}
+
+fn main() {
+ // `push_str` tests
+
+ let mut string = String::new();
+ string.push('R');
+ string.push('\'');
+
+ string.push('u');
+ string.push_str("st");
+ string.push_str("");
+ string.push('\x52');
+ string.push('\u{0052}');
+ string.push('a');
+
+ get_string!().push('ö');
+
+ // `insert_str` tests
+
+ let mut string = String::new();
+ string.insert(0, 'R');
+ string.insert(1, '\'');
+
+ string.insert(0, 'u');
+ string.insert_str(2, "st");
+ string.insert_str(0, "");
+ string.insert(0, '\x52');
+ string.insert(0, '\u{0052}');
+ let x: usize = 2;
+ string.insert(x, 'a');
+ const Y: usize = 1;
+ string.insert(Y, 'a');
+ string.insert(Y, '"');
+ string.insert(Y, '\'');
+
+ get_string!().insert(1, '?');
+}
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.rs b/src/tools/clippy/tests/ui/single_char_add_str.rs
new file mode 100644
index 000000000..a799ea7d8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_add_str.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+#![warn(clippy::single_char_add_str)]
+
+macro_rules! get_string {
+ () => {
+ String::from("Hello world!")
+ };
+}
+
+fn main() {
+ // `push_str` tests
+
+ let mut string = String::new();
+ string.push_str("R");
+ string.push_str("'");
+
+ string.push('u');
+ string.push_str("st");
+ string.push_str("");
+ string.push_str("\x52");
+ string.push_str("\u{0052}");
+ string.push_str(r##"a"##);
+
+ get_string!().push_str("ö");
+
+ // `insert_str` tests
+
+ let mut string = String::new();
+ string.insert_str(0, "R");
+ string.insert_str(1, "'");
+
+ string.insert(0, 'u');
+ string.insert_str(2, "st");
+ string.insert_str(0, "");
+ string.insert_str(0, "\x52");
+ string.insert_str(0, "\u{0052}");
+ let x: usize = 2;
+ string.insert_str(x, r##"a"##);
+ const Y: usize = 1;
+ string.insert_str(Y, r##"a"##);
+ string.insert_str(Y, r##"""##);
+ string.insert_str(Y, r##"'"##);
+
+ get_string!().insert_str(1, "?");
+}
diff --git a/src/tools/clippy/tests/ui/single_char_add_str.stderr b/src/tools/clippy/tests/ui/single_char_add_str.stderr
new file mode 100644
index 000000000..55d91583a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_add_str.stderr
@@ -0,0 +1,94 @@
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:14:5
+ |
+LL | string.push_str("R");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
+ |
+ = note: `-D clippy::single-char-add-str` implied by `-D warnings`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:15:5
+ |
+LL | string.push_str("'");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:20:5
+ |
+LL | string.push_str("/x52");
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:21:5
+ |
+LL | string.push_str("/u{0052}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:22:5
+ |
+LL | string.push_str(r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:24:5
+ |
+LL | get_string!().push_str("ö");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:29:5
+ |
+LL | string.insert_str(0, "R");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:30:5
+ |
+LL | string.insert_str(1, "'");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:35:5
+ |
+LL | string.insert_str(0, "/x52");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:36:5
+ |
+LL | string.insert_str(0, "/u{0052}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:38:5
+ |
+LL | string.insert_str(x, r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:40:5
+ |
+LL | string.insert_str(Y, r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:41:5
+ |
+LL | string.insert_str(Y, r##"""##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:42:5
+ |
+LL | string.insert_str(Y, r##"'"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:44:5
+ |
+LL | get_string!().insert_str(1, "?");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')`
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.rs b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs
new file mode 100644
index 000000000..69c5b236f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs
@@ -0,0 +1,44 @@
+#![warn(clippy::single_char_lifetime_names)]
+#![allow(clippy::let_unit_value)]
+
+// Lifetimes should only be linted when they're introduced
+struct DiagnosticCtx<'a, 'b>
+where
+ 'a: 'b,
+{
+ _source: &'a str,
+ _unit: &'b (),
+}
+
+// Only the lifetimes on the `impl`'s generics should be linted
+impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+ fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> {
+ Self {
+ _source: source,
+ _unit: unit,
+ }
+ }
+}
+
+// No lifetimes should be linted here
+impl<'src, 'unit> DiagnosticCtx<'src, 'unit> {
+ fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> {
+ Self {
+ _source: source,
+ _unit: unit,
+ }
+ }
+}
+
+// Only 'a should be linted here
+fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
+ base.split_once(other)
+ .map(|(left, right)| (left, Some(right)))
+ .unwrap_or((base, None))
+}
+
+fn main() {
+ let src = "loop {}";
+ let unit = ();
+ DiagnosticCtx::new(src, &unit);
+}
diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr
new file mode 100644
index 000000000..1438b3999
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr
@@ -0,0 +1,43 @@
+error: single-character lifetime names are likely uninformative
+ --> $DIR/single_char_lifetime_names.rs:5:22
+ |
+LL | struct DiagnosticCtx<'a, 'b>
+ | ^^
+ |
+ = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings`
+ = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+ --> $DIR/single_char_lifetime_names.rs:5:26
+ |
+LL | struct DiagnosticCtx<'a, 'b>
+ | ^^
+ |
+ = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+ --> $DIR/single_char_lifetime_names.rs:14:6
+ |
+LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+ | ^^
+ |
+ = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+ --> $DIR/single_char_lifetime_names.rs:14:10
+ |
+LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+ | ^^
+ |
+ = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+ --> $DIR/single_char_lifetime_names.rs:34:15
+ |
+LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
+ | ^^
+ |
+ = help: use a more informative name
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed
new file mode 100644
index 000000000..68e267267
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![allow(unused_must_use)]
+
+use std::collections::HashSet;
+
+fn main() {
+ let x = "foo";
+ x.split('x');
+ x.split("xx");
+ x.split('x');
+
+ let y = "x";
+ x.split(y);
+ x.split('ß');
+ x.split('ℝ');
+ x.split('💣');
+ // Can't use this lint for unicode code points which don't fit in a char
+ x.split("❤️");
+ x.split_inclusive('x');
+ x.contains('x');
+ x.starts_with('x');
+ x.ends_with('x');
+ x.find('x');
+ x.rfind('x');
+ x.rsplit('x');
+ x.split_terminator('x');
+ x.rsplit_terminator('x');
+ x.splitn(2, 'x');
+ x.rsplitn(2, 'x');
+ x.split_once('x');
+ x.rsplit_once('x');
+ x.matches('x');
+ x.rmatches('x');
+ x.match_indices('x');
+ x.rmatch_indices('x');
+ x.trim_start_matches('x');
+ x.trim_end_matches('x');
+ x.strip_prefix('x');
+ x.strip_suffix('x');
+ x.replace('x', "y");
+ x.replacen('x', "y", 3);
+ // Make sure we escape characters correctly.
+ x.split('\n');
+ x.split('\'');
+ x.split('\'');
+
+ let h = HashSet::<String>::new();
+ h.contains("X"); // should not warn
+
+ x.replace(';', ",").split(','); // issue #2978
+ x.starts_with('\x03'); // issue #2996
+
+ // Issue #3204
+ const S: &str = "#";
+ x.find(S);
+
+ // Raw string
+ x.split('a');
+ x.split('a');
+ x.split('a');
+ x.split('\'');
+ x.split('#');
+ // Must escape backslash in raw strings when converting to char #8060
+ x.split('\\');
+ x.split('\\');
+}
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs
new file mode 100644
index 000000000..186202d78
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_pattern.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![allow(unused_must_use)]
+
+use std::collections::HashSet;
+
+fn main() {
+ let x = "foo";
+ x.split("x");
+ x.split("xx");
+ x.split('x');
+
+ let y = "x";
+ x.split(y);
+ x.split("ß");
+ x.split("ℝ");
+ x.split("💣");
+ // Can't use this lint for unicode code points which don't fit in a char
+ x.split("❤️");
+ x.split_inclusive("x");
+ x.contains("x");
+ x.starts_with("x");
+ x.ends_with("x");
+ x.find("x");
+ x.rfind("x");
+ x.rsplit("x");
+ x.split_terminator("x");
+ x.rsplit_terminator("x");
+ x.splitn(2, "x");
+ x.rsplitn(2, "x");
+ x.split_once("x");
+ x.rsplit_once("x");
+ x.matches("x");
+ x.rmatches("x");
+ x.match_indices("x");
+ x.rmatch_indices("x");
+ x.trim_start_matches("x");
+ x.trim_end_matches("x");
+ x.strip_prefix("x");
+ x.strip_suffix("x");
+ x.replace("x", "y");
+ x.replacen("x", "y", 3);
+ // Make sure we escape characters correctly.
+ x.split("\n");
+ x.split("'");
+ x.split("\'");
+
+ let h = HashSet::<String>::new();
+ h.contains("X"); // should not warn
+
+ x.replace(';', ",").split(","); // issue #2978
+ x.starts_with("\x03"); // issue #2996
+
+ // Issue #3204
+ const S: &str = "#";
+ x.find(S);
+
+ // Raw string
+ x.split(r"a");
+ x.split(r#"a"#);
+ x.split(r###"a"###);
+ x.split(r###"'"###);
+ x.split(r###"#"###);
+ // Must escape backslash in raw strings when converting to char #8060
+ x.split(r#"\"#);
+ x.split(r"\");
+}
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr
new file mode 100644
index 000000000..5564aac67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr
@@ -0,0 +1,238 @@
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:9:13
+ |
+LL | x.split("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+ |
+ = note: `-D clippy::single-char-pattern` implied by `-D warnings`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:15:13
+ |
+LL | x.split("ß");
+ | ^^^ help: try using a `char` instead: `'ß'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:16:13
+ |
+LL | x.split("ℝ");
+ | ^^^ help: try using a `char` instead: `'ℝ'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:17:13
+ |
+LL | x.split("💣");
+ | ^^^^ help: try using a `char` instead: `'💣'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:20:23
+ |
+LL | x.split_inclusive("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:21:16
+ |
+LL | x.contains("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:22:19
+ |
+LL | x.starts_with("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:23:17
+ |
+LL | x.ends_with("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:24:12
+ |
+LL | x.find("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:25:13
+ |
+LL | x.rfind("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:26:14
+ |
+LL | x.rsplit("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:27:24
+ |
+LL | x.split_terminator("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:28:25
+ |
+LL | x.rsplit_terminator("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:29:17
+ |
+LL | x.splitn(2, "x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:30:18
+ |
+LL | x.rsplitn(2, "x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:31:18
+ |
+LL | x.split_once("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:32:19
+ |
+LL | x.rsplit_once("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:33:15
+ |
+LL | x.matches("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:34:16
+ |
+LL | x.rmatches("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:35:21
+ |
+LL | x.match_indices("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:36:22
+ |
+LL | x.rmatch_indices("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:37:26
+ |
+LL | x.trim_start_matches("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:38:24
+ |
+LL | x.trim_end_matches("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:39:20
+ |
+LL | x.strip_prefix("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:40:20
+ |
+LL | x.strip_suffix("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:41:15
+ |
+LL | x.replace("x", "y");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:42:16
+ |
+LL | x.replacen("x", "y", 3);
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:44:13
+ |
+LL | x.split("/n");
+ | ^^^^ help: try using a `char` instead: `'/n'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:45:13
+ |
+LL | x.split("'");
+ | ^^^ help: try using a `char` instead: `'/''`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:46:13
+ |
+LL | x.split("/'");
+ | ^^^^ help: try using a `char` instead: `'/''`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:51:31
+ |
+LL | x.replace(';', ",").split(","); // issue #2978
+ | ^^^ help: try using a `char` instead: `','`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:52:19
+ |
+LL | x.starts_with("/x03"); // issue #2996
+ | ^^^^^^ help: try using a `char` instead: `'/x03'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:59:13
+ |
+LL | x.split(r"a");
+ | ^^^^ help: try using a `char` instead: `'a'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:60:13
+ |
+LL | x.split(r#"a"#);
+ | ^^^^^^ help: try using a `char` instead: `'a'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:61:13
+ |
+LL | x.split(r###"a"###);
+ | ^^^^^^^^^^ help: try using a `char` instead: `'a'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:62:13
+ |
+LL | x.split(r###"'"###);
+ | ^^^^^^^^^^ help: try using a `char` instead: `'/''`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:63:13
+ |
+LL | x.split(r###"#"###);
+ | ^^^^^^^^^^ help: try using a `char` instead: `'#'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:65:13
+ |
+LL | x.split(r#"/"#);
+ | ^^^^^^ help: try using a `char` instead: `'/'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:66:13
+ |
+LL | x.split(r"/");
+ | ^^^^ help: try using a `char` instead: `'/'`
+
+error: aborting due to 39 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
new file mode 100644
index 000000000..4c40739d6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
@@ -0,0 +1,33 @@
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+
+use serde as edres;
+pub use serde;
+
+macro_rules! m {
+ () => {
+ use regex;
+ };
+}
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+
+ // False positive #5154, shouldn't trigger lint.
+ m!();
+}
+
+mod hello_mod {
+
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs
new file mode 100644
index 000000000..9280bab3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs
@@ -0,0 +1,33 @@
+// run-rustfix
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+macro_rules! m {
+ () => {
+ use regex;
+ };
+}
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+
+ // False positive #5154, shouldn't trigger lint.
+ m!();
+}
+
+mod hello_mod {
+ use regex;
+ #[allow(dead_code)]
+ fn hello_mod() {}
+}
+
+mod hi_mod {
+ use self::regex::{Regex, RegexSet};
+ use regex;
+ #[allow(dead_code)]
+ fn hi_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
new file mode 100644
index 000000000..509c88ac2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
@@ -0,0 +1,16 @@
+error: this import is redundant
+ --> $DIR/single_component_path_imports.rs:23:5
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: this import is redundant
+ --> $DIR/single_component_path_imports.rs:5:1
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
new file mode 100644
index 000000000..fda294a61
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+// #7923: normal `use` statements of macros should also not trigger the lint
+
+macro_rules! m1 {
+ () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+ () => {};
+}
+use m2; // ok
+
+fn main() {
+ m1!();
+ m2!();
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs
new file mode 100644
index 000000000..c75beb747
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+fn main() {
+ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+}
+
+mod root_nested_use_mod {
+ use {regex, serde};
+ #[allow(dead_code)]
+ fn root_nested_use_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
new file mode 100644
index 000000000..cf990be1b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
@@ -0,0 +1,25 @@
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:13:10
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+ = help: remove this import
+
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:13:17
+ |
+LL | use {regex, serde};
+ | ^^^^^
+ |
+ = help: remove this import
+
+error: this import is redundant
+ --> $DIR/single_component_path_imports_nested_first.rs:4:1
+ |
+LL | use regex;
+ | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs
new file mode 100644
index 000000000..48e8e5302
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+use regex;
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs
new file mode 100644
index 000000000..4fb0cf40b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+ regex::{Regex, RegexSet},
+ some_mod::SomeType,
+};
+
+mod some_mod {
+ pub struct SomeType;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/single_element_loop.fixed b/src/tools/clippy/tests/ui/single_element_loop.fixed
new file mode 100644
index 000000000..63d31ff83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(clippy::single_element_loop)]
+fn main() {
+ let item1 = 2;
+ {
+ let item = &item1;
+ dbg!(item);
+ }
+
+ {
+ let item = &item1;
+ dbg!(item);
+ }
+
+ {
+ let item = &(0..5);
+ dbg!(item);
+ }
+
+ {
+ let item = &mut (0..5);
+ dbg!(item);
+ }
+
+ {
+ let item = 0..5;
+ dbg!(item);
+ }
+
+ {
+ let item = 0..5;
+ dbg!(item);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_element_loop.rs b/src/tools/clippy/tests/ui/single_element_loop.rs
new file mode 100644
index 000000000..2cda5a329
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(clippy::single_element_loop)]
+fn main() {
+ let item1 = 2;
+ for item in &[item1] {
+ dbg!(item);
+ }
+
+ for item in [item1].iter() {
+ dbg!(item);
+ }
+
+ for item in &[0..5] {
+ dbg!(item);
+ }
+
+ for item in [0..5].iter_mut() {
+ dbg!(item);
+ }
+
+ for item in [0..5] {
+ dbg!(item);
+ }
+
+ for item in [0..5].into_iter() {
+ dbg!(item);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_element_loop.stderr b/src/tools/clippy/tests/ui/single_element_loop.stderr
new file mode 100644
index 000000000..0aeb8da1a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.stderr
@@ -0,0 +1,99 @@
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:7:5
+ |
+LL | / for item in &[item1] {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::single-element-loop` implied by `-D warnings`
+help: try
+ |
+LL ~ {
+LL + let item = &item1;
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:11:5
+ |
+LL | / for item in [item1].iter() {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = &item1;
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:15:5
+ |
+LL | / for item in &[0..5] {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = &(0..5);
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:19:5
+ |
+LL | / for item in [0..5].iter_mut() {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = &mut (0..5);
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:23:5
+ |
+LL | / for item in [0..5] {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = 0..5;
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:27:5
+ |
+LL | / for item in [0..5].into_iter() {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = 0..5;
+LL + dbg!(item);
+LL + }
+ |
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs
new file mode 100644
index 000000000..dd148edf5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match.rs
@@ -0,0 +1,245 @@
+#![warn(clippy::single_match)]
+
+fn dummy() {}
+
+fn single_match() {
+ let x = Some(1u8);
+
+ match x {
+ Some(y) => {
+ println!("{:?}", y);
+ },
+ _ => (),
+ };
+
+ let x = Some(1u8);
+ match x {
+ // Note the missing block braces.
+ // We suggest `if let Some(y) = x { .. }` because the macro
+ // is expanded before we can do anything.
+ Some(y) => println!("{:?}", y),
+ _ => (),
+ }
+
+ let z = (1u8, 1u8);
+ match z {
+ (2..=3, 7..=9) => dummy(),
+ _ => {},
+ };
+
+ // Not linted (pattern guards used)
+ match x {
+ Some(y) if y == 0 => println!("{:?}", y),
+ _ => (),
+ }
+
+ // Not linted (no block with statements in the single arm)
+ match z {
+ (2..=3, 7..=9) => println!("{:?}", z),
+ _ => println!("nope"),
+ }
+}
+
+enum Foo {
+ Bar,
+ Baz(u8),
+}
+use std::borrow::Cow;
+use Foo::*;
+
+fn single_match_know_enum() {
+ let x = Some(1u8);
+ let y: Result<_, i8> = Ok(1i8);
+
+ match x {
+ Some(y) => dummy(),
+ None => (),
+ };
+
+ match y {
+ Ok(y) => dummy(),
+ Err(..) => (),
+ };
+
+ let c = Cow::Borrowed("");
+
+ match c {
+ Cow::Borrowed(..) => dummy(),
+ Cow::Owned(..) => (),
+ };
+
+ let z = Foo::Bar;
+ // no warning
+ match z {
+ Bar => println!("42"),
+ Baz(_) => (),
+ }
+
+ match z {
+ Baz(_) => println!("42"),
+ Bar => (),
+ }
+}
+
+// issue #173
+fn if_suggestion() {
+ let x = "test";
+ match x {
+ "test" => println!(),
+ _ => (),
+ }
+
+ #[derive(PartialEq, Eq)]
+ enum Foo {
+ A,
+ B,
+ C(u32),
+ }
+
+ let x = Foo::A;
+ match x {
+ Foo::A => println!(),
+ _ => (),
+ }
+
+ const FOO_C: Foo = Foo::C(0);
+ match x {
+ FOO_C => println!(),
+ _ => (),
+ }
+
+ match &&x {
+ Foo::A => println!(),
+ _ => (),
+ }
+
+ let x = &x;
+ match &x {
+ Foo::A => println!(),
+ _ => (),
+ }
+
+ enum Bar {
+ A,
+ B,
+ }
+ impl PartialEq for Bar {
+ fn eq(&self, rhs: &Self) -> bool {
+ matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B))
+ }
+ }
+ impl Eq for Bar {}
+
+ let x = Bar::A;
+ match x {
+ Bar::A => println!(),
+ _ => (),
+ }
+
+ // issue #7038
+ struct X;
+ let x = Some(X);
+ match x {
+ None => println!(),
+ _ => (),
+ };
+}
+
+// See: issue #8282
+fn ranges() {
+ enum E {
+ V,
+ }
+ let x = (Some(E::V), Some(42));
+
+ // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the
+ // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared
+ // because of `match` construction.
+ match x {
+ (Some(E::V), _) => {},
+ (None, _) => {},
+ }
+
+ // lint
+ match x {
+ (Some(_), _) => {},
+ (None, _) => {},
+ }
+
+ // lint
+ match x {
+ (Some(E::V), _) => todo!(),
+ (_, _) => {},
+ }
+
+ // lint
+ match (Some(42), Some(E::V), Some(42)) {
+ (.., Some(E::V), _) => {},
+ (..) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (.., Some(E::V), _) => {},
+ (.., None, _) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (Some(E::V), ..) => {},
+ (None, ..) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (_, Some(E::V), ..) => {},
+ (_, None, ..) => {},
+ }
+}
+
+fn skip_type_aliases() {
+ enum OptionEx {
+ Some(i32),
+ None,
+ }
+ enum ResultEx {
+ Err(i32),
+ Ok(i32),
+ }
+
+ use OptionEx::{None, Some};
+ use ResultEx::{Err, Ok};
+
+ // don't lint
+ match Err(42) {
+ Ok(_) => dummy(),
+ Err(_) => (),
+ };
+
+ // don't lint
+ match Some(1i32) {
+ Some(_) => dummy(),
+ None => (),
+ };
+}
+
+macro_rules! single_match {
+ ($num:literal) => {
+ match $num {
+ 15 => println!("15"),
+ _ => (),
+ }
+ };
+}
+
+fn main() {
+ single_match!(5);
+
+ // Don't lint
+ let _ = match Some(0) {
+ #[cfg(feature = "foo")]
+ Some(10) => 11,
+ Some(x) => x,
+ _ => 0,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr
new file mode 100644
index 000000000..4d2b9ec5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match.stderr
@@ -0,0 +1,159 @@
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:8:5
+ |
+LL | / match x {
+LL | | Some(y) => {
+LL | | println!("{:?}", y);
+LL | | },
+LL | | _ => (),
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::single-match` implied by `-D warnings`
+help: try this
+ |
+LL ~ if let Some(y) = x {
+LL + println!("{:?}", y);
+LL ~ };
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:16:5
+ |
+LL | / match x {
+LL | | // Note the missing block braces.
+LL | | // We suggest `if let Some(y) = x { .. }` because the macro
+LL | | // is expanded before we can do anything.
+LL | | Some(y) => println!("{:?}", y),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:25:5
+ |
+LL | / match z {
+LL | | (2..=3, 7..=9) => dummy(),
+LL | | _ => {},
+LL | | };
+ | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:54:5
+ |
+LL | / match x {
+LL | | Some(y) => dummy(),
+LL | | None => (),
+LL | | };
+ | |_____^ help: try this: `if let Some(y) = x { dummy() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:59:5
+ |
+LL | / match y {
+LL | | Ok(y) => dummy(),
+LL | | Err(..) => (),
+LL | | };
+ | |_____^ help: try this: `if let Ok(y) = y { dummy() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:66:5
+ |
+LL | / match c {
+LL | | Cow::Borrowed(..) => dummy(),
+LL | | Cow::Owned(..) => (),
+LL | | };
+ | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }`
+
+error: you seem to be trying to use `match` for an equality check. Consider using `if`
+ --> $DIR/single_match.rs:87:5
+ |
+LL | / match x {
+LL | | "test" => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if x == "test" { println!() }`
+
+error: you seem to be trying to use `match` for an equality check. Consider using `if`
+ --> $DIR/single_match.rs:100:5
+ |
+LL | / match x {
+LL | | Foo::A => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if x == Foo::A { println!() }`
+
+error: you seem to be trying to use `match` for an equality check. Consider using `if`
+ --> $DIR/single_match.rs:106:5
+ |
+LL | / match x {
+LL | | FOO_C => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if x == FOO_C { println!() }`
+
+error: you seem to be trying to use `match` for an equality check. Consider using `if`
+ --> $DIR/single_match.rs:111:5
+ |
+LL | / match &&x {
+LL | | Foo::A => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if x == Foo::A { println!() }`
+
+error: you seem to be trying to use `match` for an equality check. Consider using `if`
+ --> $DIR/single_match.rs:117:5
+ |
+LL | / match &x {
+LL | | Foo::A => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if x == &Foo::A { println!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:134:5
+ |
+LL | / match x {
+LL | | Bar::A => println!(),
+LL | | _ => (),
+LL | | }
+ | |_____^ help: try this: `if let Bar::A = x { println!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:142:5
+ |
+LL | / match x {
+LL | | None => println!(),
+LL | | _ => (),
+LL | | };
+ | |_____^ help: try this: `if let None = x { println!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:164:5
+ |
+LL | / match x {
+LL | | (Some(_), _) => {},
+LL | | (None, _) => {},
+LL | | }
+ | |_____^ help: try this: `if let (Some(_), _) = x {}`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:170:5
+ |
+LL | / match x {
+LL | | (Some(E::V), _) => todo!(),
+LL | | (_, _) => {},
+LL | | }
+ | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:176:5
+ |
+LL | / match (Some(42), Some(E::V), Some(42)) {
+LL | | (.., Some(E::V), _) => {},
+LL | | (..) => {},
+LL | | }
+ | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
new file mode 100644
index 000000000..70d6febb7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -0,0 +1,119 @@
+// aux-build: proc_macro_with_span.rs
+
+#![warn(clippy::single_match_else)]
+#![allow(clippy::needless_return)]
+#![allow(clippy::no_effect)]
+
+extern crate proc_macro_with_span;
+use proc_macro_with_span::with_span;
+
+enum ExprNode {
+ ExprAddrOf,
+ Butterflies,
+ Unicorns,
+}
+
+static NODE: ExprNode = ExprNode::Unicorns;
+
+fn unwrap_addr() -> Option<&'static ExprNode> {
+ let _ = match ExprNode::Butterflies {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ };
+
+ // Don't lint
+ with_span!(span match ExprNode::Butterflies {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ })
+}
+
+macro_rules! unwrap_addr {
+ ($expression:expr) => {
+ match $expression {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ }
+ };
+}
+
+#[rustfmt::skip]
+fn main() {
+ unwrap_addr!(ExprNode::Unicorns);
+
+ //
+ // don't lint single exprs/statements
+ //
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => return,
+ }
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ return
+ },
+ }
+
+ // don't lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ return;
+ },
+ }
+
+ //
+ // lint multiple exprs/statements "else" blocks
+ //
+
+ // lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ println!("else block");
+ return
+ },
+ }
+
+ // lint here
+ match Some(1) {
+ Some(a) => println!("${:?}", a),
+ None => {
+ println!("else block");
+ return;
+ },
+ }
+
+ // lint here
+ use std::convert::Infallible;
+ match Result::<i32, Infallible>::Ok(1) {
+ Ok(a) => println!("${:?}", a),
+ Err(_) => {
+ println!("else block");
+ return;
+ }
+ }
+
+ use std::borrow::Cow;
+ match Cow::from("moo") {
+ Cow::Owned(a) => println!("${:?}", a),
+ Cow::Borrowed(_) => {
+ println!("else block");
+ return;
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr
new file mode 100644
index 000000000..38fd9c6a6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_match_else.stderr
@@ -0,0 +1,104 @@
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:19:13
+ |
+LL | let _ = match ExprNode::Butterflies {
+ | _____________^
+LL | | ExprNode::ExprAddrOf => Some(&NODE),
+LL | | _ => {
+LL | | let x = 5;
+LL | | None
+LL | | },
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::single-match-else` implied by `-D warnings`
+help: try this
+ |
+LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
+LL + let x = 5;
+LL + None
+LL ~ };
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:84:5
+ |
+LL | / match Some(1) {
+LL | | Some(a) => println!("${:?}", a),
+LL | | None => {
+LL | | println!("else block");
+LL | | return
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL + println!("else block");
+LL + return
+LL + }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:93:5
+ |
+LL | / match Some(1) {
+LL | | Some(a) => println!("${:?}", a),
+LL | | None => {
+LL | | println!("else block");
+LL | | return;
+LL | | },
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL + println!("else block");
+LL + return;
+LL + }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:103:5
+ |
+LL | / match Result::<i32, Infallible>::Ok(1) {
+LL | | Ok(a) => println!("${:?}", a),
+LL | | Err(_) => {
+LL | | println!("else block");
+LL | | return;
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
+LL + println!("else block");
+LL + return;
+LL + }
+ |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match_else.rs:112:5
+ |
+LL | / match Cow::from("moo") {
+LL | | Cow::Owned(a) => println!("${:?}", a),
+LL | | Cow::Borrowed(_) => {
+LL | | println!("else block");
+LL | | return;
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL ~ if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else {
+LL + println!("else block");
+LL + return;
+LL + }
+ |
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs
new file mode 100644
index 000000000..2594e8fa6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs
@@ -0,0 +1,37 @@
+#![warn(clippy::size_of_in_element_count)]
+#![allow(clippy::ptr_offset_with_cast)]
+
+use std::mem::{size_of, size_of_val};
+use std::ptr::{copy, copy_nonoverlapping, write_bytes};
+
+fn main() {
+ const SIZE: usize = 128;
+ const HALF_SIZE: usize = SIZE / 2;
+ const DOUBLE_SIZE: usize = SIZE * 2;
+ let mut x = [2u8; SIZE];
+ let mut y = [2u8; SIZE];
+
+ // Count expression involving multiplication of size_of (Should trigger the lint)
+ unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
+
+ // Count expression involving nested multiplications of size_of (Should trigger the lint)
+ unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
+
+ // Count expression involving divisions of size_of (Should trigger the lint)
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
+
+ // Count expression involving divisions by size_of (Should not trigger the lint)
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::<u8>()) };
+
+ // Count expression involving divisions by multiple size_of (Should not trigger the lint)
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::<u8>())) };
+
+ // Count expression involving recursive divisions by size_of (Should trigger the lint)
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
+
+ // No size_of calls (Should not trigger the lint)
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
+
+ // Different types for pointee and size_of (Should not trigger the lint)
+ unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u16>() / 2 * SIZE) };
+}
diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr
new file mode 100644
index 000000000..0f0dff57f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr
@@ -0,0 +1,35 @@
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/expressions.rs:15:62
+ |
+LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::size-of-in-element-count` implied by `-D warnings`
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/expressions.rs:18:62
+ |
+LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/expressions.rs:21:47
+ |
+LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/expressions.rs:30:47
+ |
+LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs
new file mode 100644
index 000000000..09d08ac37
--- /dev/null
+++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs
@@ -0,0 +1,46 @@
+#![warn(clippy::size_of_in_element_count)]
+#![allow(clippy::ptr_offset_with_cast)]
+
+use std::mem::{size_of, size_of_val};
+use std::ptr::{
+ copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes,
+};
+use std::slice::{from_raw_parts, from_raw_parts_mut};
+
+fn main() {
+ const SIZE: usize = 128;
+ const HALF_SIZE: usize = SIZE / 2;
+ const DOUBLE_SIZE: usize = SIZE * 2;
+ let mut x = [2u8; SIZE];
+ let mut y = [2u8; SIZE];
+
+ // Count is size_of (Should trigger the lint)
+ unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
+ unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
+
+ unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
+ unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) };
+ unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
+ unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) };
+
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
+ unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
+
+ unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
+ unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
+
+ unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) };
+
+ slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
+ slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
+
+ unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
+ unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
+
+ unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
+ y.as_ptr().wrapping_sub(size_of::<u8>());
+ unsafe { y.as_ptr().add(size_of::<u8>()) };
+ y.as_mut_ptr().wrapping_add(size_of::<u8>());
+ unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
+ y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
+}
diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr
new file mode 100644
index 000000000..c1e824167
--- /dev/null
+++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr
@@ -0,0 +1,171 @@
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:18:68
+ |
+LL | unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::size-of-in-element-count` implied by `-D warnings`
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:19:62
+ |
+LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:21:49
+ |
+LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:22:64
+ |
+LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:23:51
+ |
+LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:24:66
+ |
+LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:26:47
+ |
+LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:27:47
+ |
+LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:29:46
+ |
+LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:30:47
+ |
+LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:32:66
+ |
+LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:34:46
+ |
+LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:35:38
+ |
+LL | slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:37:49
+ |
+LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:38:41
+ |
+LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:40:33
+ |
+LL | unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:41:29
+ |
+LL | y.as_ptr().wrapping_sub(size_of::<u8>());
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:42:29
+ |
+LL | unsafe { y.as_ptr().add(size_of::<u8>()) };
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:43:33
+ |
+LL | y.as_mut_ptr().wrapping_add(size_of::<u8>());
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:44:32
+ |
+LL | unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: found a count of bytes instead of a count of elements of `T`
+ --> $DIR/functions.rs:45:36
+ |
+LL | y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/skip_while_next.rs b/src/tools/clippy/tests/ui/skip_while_next.rs
new file mode 100644
index 000000000..a522c0f08
--- /dev/null
+++ b/src/tools/clippy/tests/ui/skip_while_next.rs
@@ -0,0 +1,29 @@
+// aux-build:option_helpers.rs
+
+#![warn(clippy::skip_while_next)]
+#![allow(clippy::blacklisted_name)]
+
+extern crate option_helpers;
+use option_helpers::IteratorFalsePositives;
+
+#[rustfmt::skip]
+fn skip_while_next() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Single-line case.
+ let _ = v.iter().skip_while(|&x| *x < 0).next();
+
+ // Multi-line case.
+ let _ = v.iter().skip_while(|&x| {
+ *x < 0
+ }
+ ).next();
+
+ // Check that hat we don't lint if the caller is not an `Iterator`.
+ let foo = IteratorFalsePositives { foo: 0 };
+ let _ = foo.skip_while().next();
+}
+
+fn main() {
+ skip_while_next();
+}
diff --git a/src/tools/clippy/tests/ui/skip_while_next.stderr b/src/tools/clippy/tests/ui/skip_while_next.stderr
new file mode 100644
index 000000000..269cc1346
--- /dev/null
+++ b/src/tools/clippy/tests/ui/skip_while_next.stderr
@@ -0,0 +1,23 @@
+error: called `skip_while(<p>).next()` on an `Iterator`
+ --> $DIR/skip_while_next.rs:14:13
+ |
+LL | let _ = v.iter().skip_while(|&x| *x < 0).next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::skip-while-next` implied by `-D warnings`
+ = help: this is more succinctly expressed by calling `.find(!<p>)` instead
+
+error: called `skip_while(<p>).next()` on an `Iterator`
+ --> $DIR/skip_while_next.rs:17:13
+ |
+LL | let _ = v.iter().skip_while(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).next();
+ | |___________________________^
+ |
+ = help: this is more succinctly expressed by calling `.find(!<p>)` instead
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
new file mode 100644
index 000000000..16be9f6d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
@@ -0,0 +1,69 @@
+use std::iter::repeat;
+
+fn main() {
+ resize_vector();
+ extend_vector();
+ mixed_extend_resize_vector();
+}
+
+fn extend_vector() {
+ // Extend with constant expression
+ let len = 300;
+ let mut vec1 = Vec::with_capacity(len);
+ vec1.extend(repeat(0).take(len));
+
+ // Extend with len expression
+ let mut vec2 = Vec::with_capacity(len - 10);
+ vec2.extend(repeat(0).take(len - 10));
+
+ // Extend with mismatching expression should not be warned
+ let mut vec3 = Vec::with_capacity(24322);
+ vec3.extend(repeat(0).take(2));
+
+ let mut vec4 = Vec::with_capacity(len);
+ vec4.extend(repeat(0).take(vec4.capacity()));
+}
+
+fn mixed_extend_resize_vector() {
+ // Mismatching len
+ let mut mismatching_len = Vec::with_capacity(30);
+ mismatching_len.extend(repeat(0).take(40));
+
+ // Slow initialization
+ let mut resized_vec = Vec::with_capacity(30);
+ resized_vec.resize(30, 0);
+
+ let mut extend_vec = Vec::with_capacity(30);
+ extend_vec.extend(repeat(0).take(30));
+}
+
+fn resize_vector() {
+ // Resize with constant expression
+ let len = 300;
+ let mut vec1 = Vec::with_capacity(len);
+ vec1.resize(len, 0);
+
+ // Resize mismatch len
+ let mut vec2 = Vec::with_capacity(200);
+ vec2.resize(10, 0);
+
+ // Resize with len expression
+ let mut vec3 = Vec::with_capacity(len - 10);
+ vec3.resize(len - 10, 0);
+
+ let mut vec4 = Vec::with_capacity(len);
+ vec4.resize(vec4.capacity(), 0);
+
+ // Reinitialization should be warned
+ vec1 = Vec::with_capacity(10);
+ vec1.resize(10, 0);
+}
+
+fn do_stuff(vec: &mut [u8]) {}
+
+fn extend_vector_with_manipulations_between() {
+ let len = 300;
+ let mut vec1: Vec<u8> = Vec::with_capacity(len);
+ do_stuff(&mut vec1);
+ vec1.extend(repeat(0).take(len));
+}
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
new file mode 100644
index 000000000..cb3ce3e95
--- /dev/null
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
@@ -0,0 +1,76 @@
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:13:5
+ |
+LL | let mut vec1 = Vec::with_capacity(len);
+ | ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL | vec1.extend(repeat(0).take(len));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::slow-vector-initialization` implied by `-D warnings`
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:17:5
+ |
+LL | let mut vec2 = Vec::with_capacity(len - 10);
+ | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
+LL | vec2.extend(repeat(0).take(len - 10));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:24:5
+ |
+LL | let mut vec4 = Vec::with_capacity(len);
+ | ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL | vec4.extend(repeat(0).take(vec4.capacity()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:34:5
+ |
+LL | let mut resized_vec = Vec::with_capacity(30);
+ | ---------------------- help: consider replace allocation with: `vec![0; 30]`
+LL | resized_vec.resize(30, 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:37:5
+ |
+LL | let mut extend_vec = Vec::with_capacity(30);
+ | ---------------------- help: consider replace allocation with: `vec![0; 30]`
+LL | extend_vec.extend(repeat(0).take(30));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:44:5
+ |
+LL | let mut vec1 = Vec::with_capacity(len);
+ | ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL | vec1.resize(len, 0);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:52:5
+ |
+LL | let mut vec3 = Vec::with_capacity(len - 10);
+ | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
+LL | vec3.resize(len - 10, 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:55:5
+ |
+LL | let mut vec4 = Vec::with_capacity(len);
+ | ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL | vec4.resize(vec4.capacity(), 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:59:5
+ |
+LL | vec1 = Vec::with_capacity(10);
+ | ---------------------- help: consider replace allocation with: `vec![0; 10]`
+LL | vec1.resize(10, 0);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
new file mode 100644
index 000000000..f5f18169d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
@@ -0,0 +1,32 @@
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+ // positive examples
+ let mut vec = vec![1, 3, 2];
+ vec.sort_unstable();
+ let mut vec = vec![false, false, true];
+ vec.sort_unstable();
+ let mut vec = vec!['a', 'A', 'c'];
+ vec.sort_unstable();
+ let mut vec = vec!["ab", "cd", "ab", "bc"];
+ vec.sort_unstable();
+ let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+ vec.sort_unstable();
+ let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+ vec.sort_unstable();
+ let mut arr = [1, 3, 2];
+ arr.sort_unstable();
+ // Negative examples: behavior changes if made unstable
+ let mut vec = vec![1, 3, 2];
+ vec.sort_by_key(|i| i / 2);
+ vec.sort_by(|&a, &b| (a + b).cmp(&b));
+ // negative examples - Not of a primitive type
+ let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+ vec_of_complex.sort();
+ vec_of_complex.sort_by_key(String::len);
+ let mut vec = vec![(String::from("hello"), String::from("world"))];
+ vec.sort();
+ let mut vec = vec![[String::from("hello"), String::from("world")]];
+ vec.sort();
+}
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.rs b/src/tools/clippy/tests/ui/stable_sort_primitive.rs
new file mode 100644
index 000000000..8149c5638
--- /dev/null
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+ // positive examples
+ let mut vec = vec![1, 3, 2];
+ vec.sort();
+ let mut vec = vec![false, false, true];
+ vec.sort();
+ let mut vec = vec!['a', 'A', 'c'];
+ vec.sort();
+ let mut vec = vec!["ab", "cd", "ab", "bc"];
+ vec.sort();
+ let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+ vec.sort();
+ let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+ vec.sort();
+ let mut arr = [1, 3, 2];
+ arr.sort();
+ // Negative examples: behavior changes if made unstable
+ let mut vec = vec![1, 3, 2];
+ vec.sort_by_key(|i| i / 2);
+ vec.sort_by(|&a, &b| (a + b).cmp(&b));
+ // negative examples - Not of a primitive type
+ let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+ vec_of_complex.sort();
+ vec_of_complex.sort_by_key(String::len);
+ let mut vec = vec![(String::from("hello"), String::from("world"))];
+ vec.sort();
+ let mut vec = vec![[String::from("hello"), String::from("world")]];
+ vec.sort();
+}
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
new file mode 100644
index 000000000..c35e0c22a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
@@ -0,0 +1,59 @@
+error: used `sort` on primitive type `i32`
+ --> $DIR/stable_sort_primitive.rs:7:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `bool`
+ --> $DIR/stable_sort_primitive.rs:9:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `char`
+ --> $DIR/stable_sort_primitive.rs:11:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `str`
+ --> $DIR/stable_sort_primitive.rs:13:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `tuple`
+ --> $DIR/stable_sort_primitive.rs:15:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `array`
+ --> $DIR/stable_sort_primitive.rs:17:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: used `sort` on primitive type `i32`
+ --> $DIR/stable_sort_primitive.rs:19:5
+ |
+LL | arr.sort();
+ | ^^^^^^^^^^ help: try: `arr.sort_unstable()`
+ |
+ = note: an unstable sort typically performs faster without any observable difference for this data type
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed
new file mode 100644
index 000000000..983fac7af
--- /dev/null
+++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed
@@ -0,0 +1,54 @@
+// run-rustfix
+#![allow(dead_code, unused_must_use)]
+
+fn main() {}
+
+#[allow(clippy::unnecessary_operation)]
+fn starts_with() {
+ "".starts_with(' ');
+ !"".starts_with(' ');
+
+ // Ensure that suggestion is escaped correctly
+ "".starts_with('\n');
+ !"".starts_with('\n');
+}
+
+fn chars_cmp_with_unwrap() {
+ let s = String::from("foo");
+ if s.starts_with('f') {
+ // s.starts_with('f')
+ // Nothing here
+ }
+ if s.ends_with('o') {
+ // s.ends_with('o')
+ // Nothing here
+ }
+ if s.ends_with('o') {
+ // s.ends_with('o')
+ // Nothing here
+ }
+ if !s.starts_with('f') {
+ // !s.starts_with('f')
+ // Nothing here
+ }
+ if !s.ends_with('o') {
+ // !s.ends_with('o')
+ // Nothing here
+ }
+ if !s.ends_with('\n') {
+ // !s.ends_with('o')
+ // Nothing here
+ }
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn ends_with() {
+ "".ends_with(' ');
+ !"".ends_with(' ');
+ "".ends_with(' ');
+ !"".ends_with(' ');
+
+ // Ensure that suggestion is escaped correctly
+ "".ends_with('\n');
+ !"".ends_with('\n');
+}
diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs
new file mode 100644
index 000000000..e3335dd2e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/starts_ends_with.rs
@@ -0,0 +1,54 @@
+// run-rustfix
+#![allow(dead_code, unused_must_use)]
+
+fn main() {}
+
+#[allow(clippy::unnecessary_operation)]
+fn starts_with() {
+ "".chars().next() == Some(' ');
+ Some(' ') != "".chars().next();
+
+ // Ensure that suggestion is escaped correctly
+ "".chars().next() == Some('\n');
+ Some('\n') != "".chars().next();
+}
+
+fn chars_cmp_with_unwrap() {
+ let s = String::from("foo");
+ if s.chars().next().unwrap() == 'f' {
+ // s.starts_with('f')
+ // Nothing here
+ }
+ if s.chars().next_back().unwrap() == 'o' {
+ // s.ends_with('o')
+ // Nothing here
+ }
+ if s.chars().last().unwrap() == 'o' {
+ // s.ends_with('o')
+ // Nothing here
+ }
+ if s.chars().next().unwrap() != 'f' {
+ // !s.starts_with('f')
+ // Nothing here
+ }
+ if s.chars().next_back().unwrap() != 'o' {
+ // !s.ends_with('o')
+ // Nothing here
+ }
+ if s.chars().last().unwrap() != '\n' {
+ // !s.ends_with('o')
+ // Nothing here
+ }
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn ends_with() {
+ "".chars().last() == Some(' ');
+ Some(' ') != "".chars().last();
+ "".chars().next_back() == Some(' ');
+ Some(' ') != "".chars().next_back();
+
+ // Ensure that suggestion is escaped correctly
+ "".chars().last() == Some('\n');
+ Some('\n') != "".chars().last();
+}
diff --git a/src/tools/clippy/tests/ui/starts_ends_with.stderr b/src/tools/clippy/tests/ui/starts_ends_with.stderr
new file mode 100644
index 000000000..2dd9f53b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/starts_ends_with.stderr
@@ -0,0 +1,102 @@
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:8:5
+ |
+LL | "".chars().next() == Some(' ');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with(' ')`
+ |
+ = note: `-D clippy::chars-next-cmp` implied by `-D warnings`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:9:5
+ |
+LL | Some(' ') != "".chars().next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:12:5
+ |
+LL | "".chars().next() == Some('/n');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with('/n')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:13:5
+ |
+LL | Some('/n') != "".chars().next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with('/n')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:18:8
+ |
+LL | if s.chars().next().unwrap() == 'f' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:22:8
+ |
+LL | if s.chars().next_back().unwrap() == 'o' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
+ |
+ = note: `-D clippy::chars-last-cmp` implied by `-D warnings`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:26:8
+ |
+LL | if s.chars().last().unwrap() == 'o' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:30:8
+ |
+LL | if s.chars().next().unwrap() != 'f' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:34:8
+ |
+LL | if s.chars().next_back().unwrap() != 'o' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:38:8
+ |
+LL | if s.chars().last().unwrap() != '/n' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('/n')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:46:5
+ |
+LL | "".chars().last() == Some(' ');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:47:5
+ |
+LL | Some(' ') != "".chars().last();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:48:5
+ |
+LL | "".chars().next_back() == Some(' ');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:49:5
+ |
+LL | Some(' ') != "".chars().next_back();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:52:5
+ |
+LL | "".chars().last() == Some('/n');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with('/n')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:53:5
+ |
+LL | Some('/n') != "".chars().last();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with('/n')`
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
new file mode 100644
index 000000000..6b27475de
--- /dev/null
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -0,0 +1,45 @@
+#![warn(clippy::std_instead_of_core)]
+#![allow(unused_imports)]
+
+extern crate alloc;
+
+#[warn(clippy::std_instead_of_core)]
+fn std_instead_of_core() {
+ // Regular import
+ use std::hash::Hasher;
+ // Absolute path
+ use ::std::hash::Hash;
+ // Don't lint on `env` macro
+ use std::env;
+
+ // Multiple imports
+ use std::fmt::{Debug, Result};
+
+ // Function calls
+ let ptr = std::ptr::null::<u32>();
+ let ptr_mut = ::std::ptr::null_mut::<usize>();
+
+ // Types
+ let cell = std::cell::Cell::new(8u32);
+ let cell_absolute = ::std::cell::Cell::new(8u32);
+
+ let _ = std::env!("PATH");
+}
+
+#[warn(clippy::std_instead_of_alloc)]
+fn std_instead_of_alloc() {
+ // Only lint once.
+ use std::vec;
+ use std::vec::Vec;
+}
+
+#[warn(clippy::alloc_instead_of_core)]
+fn alloc_instead_of_core() {
+ use alloc::slice::from_ref;
+}
+
+fn main() {
+ std_instead_of_core();
+ std_instead_of_alloc();
+ alloc_instead_of_core();
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
new file mode 100644
index 000000000..bc49dabf5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
@@ -0,0 +1,93 @@
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:9:9
+ |
+LL | use std::hash::Hasher;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:11:9
+ |
+LL | use ::std::hash::Hash;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:16:20
+ |
+LL | use std::fmt::{Debug, Result};
+ | ^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:16:27
+ |
+LL | use std::fmt::{Debug, Result};
+ | ^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:19:15
+ |
+LL | let ptr = std::ptr::null::<u32>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:20:19
+ |
+LL | let ptr_mut = ::std::ptr::null_mut::<usize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:23:16
+ |
+LL | let cell = std::cell::Cell::new(8u32);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:24:25
+ |
+LL | let cell_absolute = ::std::cell::Cell::new(8u32);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `alloc`
+ --> $DIR/std_instead_of_core.rs:32:9
+ |
+LL | use std::vec;
+ | ^^^^^^^^
+ |
+ = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
+ = help: consider importing the item from `alloc`
+
+error: used import from `std` instead of `alloc`
+ --> $DIR/std_instead_of_core.rs:33:9
+ |
+LL | use std::vec::Vec;
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `alloc`
+
+error: used import from `alloc` instead of `core`
+ --> $DIR/std_instead_of_core.rs:38:9
+ |
+LL | use alloc::slice::from_ref;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
+ = help: consider importing the item from `core`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/str_to_string.rs b/src/tools/clippy/tests/ui/str_to_string.rs
new file mode 100644
index 000000000..08f734025
--- /dev/null
+++ b/src/tools/clippy/tests/ui/str_to_string.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::str_to_string)]
+
+fn main() {
+ let hello = "hello world".to_string();
+ let msg = &hello[..];
+ msg.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/str_to_string.stderr b/src/tools/clippy/tests/ui/str_to_string.stderr
new file mode 100644
index 000000000..b1f73eda5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/str_to_string.stderr
@@ -0,0 +1,19 @@
+error: `to_string()` called on a `&str`
+ --> $DIR/str_to_string.rs:4:17
+ |
+LL | let hello = "hello world".to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::str-to-string` implied by `-D warnings`
+ = help: consider using `.to_owned()`
+
+error: `to_string()` called on a `&str`
+ --> $DIR/str_to_string.rs:6:5
+ |
+LL | msg.to_string();
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider using `.to_owned()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs
new file mode 100644
index 000000000..30fd17c59
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_add.rs
@@ -0,0 +1,26 @@
+// aux-build:macro_rules.rs
+
+#[macro_use]
+extern crate macro_rules;
+
+#[warn(clippy::string_add)]
+#[allow(clippy::string_add_assign, unused)]
+fn main() {
+ // ignores assignment distinction
+ let mut x = "".to_owned();
+
+ for _ in 1..3 {
+ x = x + ".";
+ }
+
+ let y = "".to_owned();
+ let z = y + "...";
+
+ assert_eq!(&x, &z);
+
+ let mut x = 1;
+ x = x + 1;
+ assert_eq!(2, x);
+
+ string_add!();
+}
diff --git a/src/tools/clippy/tests/ui/string_add.stderr b/src/tools/clippy/tests/ui/string_add.stderr
new file mode 100644
index 000000000..3987641c7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_add.stderr
@@ -0,0 +1,30 @@
+error: manual implementation of an assign operation
+ --> $DIR/string_add.rs:13:9
+ |
+LL | x = x + ".";
+ | ^^^^^^^^^^^ help: replace it with: `x += "."`
+ |
+ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
+
+error: you added something to a string. Consider using `String::push_str()` instead
+ --> $DIR/string_add.rs:13:13
+ |
+LL | x = x + ".";
+ | ^^^^^^^
+ |
+ = note: `-D clippy::string-add` implied by `-D warnings`
+
+error: you added something to a string. Consider using `String::push_str()` instead
+ --> $DIR/string_add.rs:17:13
+ |
+LL | let z = y + "...";
+ | ^^^^^^^^^
+
+error: manual implementation of an assign operation
+ --> $DIR/string_add.rs:22:5
+ |
+LL | x = x + 1;
+ | ^^^^^^^^^ help: replace it with: `x += 1`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_add_assign.fixed b/src/tools/clippy/tests/ui/string_add_assign.fixed
new file mode 100644
index 000000000..db71bab1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_add_assign.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+
+#[allow(clippy::string_add, unused)]
+#[warn(clippy::string_add_assign)]
+fn main() {
+ // ignores assignment distinction
+ let mut x = "".to_owned();
+
+ for _ in 1..3 {
+ x += ".";
+ }
+
+ let y = "".to_owned();
+ let z = y + "...";
+
+ assert_eq!(&x, &z);
+
+ let mut x = 1;
+ x += 1;
+ assert_eq!(2, x);
+}
diff --git a/src/tools/clippy/tests/ui/string_add_assign.rs b/src/tools/clippy/tests/ui/string_add_assign.rs
new file mode 100644
index 000000000..644991945
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_add_assign.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+
+#[allow(clippy::string_add, unused)]
+#[warn(clippy::string_add_assign)]
+fn main() {
+ // ignores assignment distinction
+ let mut x = "".to_owned();
+
+ for _ in 1..3 {
+ x = x + ".";
+ }
+
+ let y = "".to_owned();
+ let z = y + "...";
+
+ assert_eq!(&x, &z);
+
+ let mut x = 1;
+ x = x + 1;
+ assert_eq!(2, x);
+}
diff --git a/src/tools/clippy/tests/ui/string_add_assign.stderr b/src/tools/clippy/tests/ui/string_add_assign.stderr
new file mode 100644
index 000000000..7676175c1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_add_assign.stderr
@@ -0,0 +1,24 @@
+error: you assigned the result of adding something to this string. Consider using `String::push_str()` instead
+ --> $DIR/string_add_assign.rs:10:9
+ |
+LL | x = x + ".";
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::string-add-assign` implied by `-D warnings`
+
+error: manual implementation of an assign operation
+ --> $DIR/string_add_assign.rs:10:9
+ |
+LL | x = x + ".";
+ | ^^^^^^^^^^^ help: replace it with: `x += "."`
+ |
+ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
+
+error: manual implementation of an assign operation
+ --> $DIR/string_add_assign.rs:19:5
+ |
+LL | x = x + 1;
+ | ^^^^^^^^^ help: replace it with: `x += 1`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_extend.fixed b/src/tools/clippy/tests/ui/string_extend.fixed
new file mode 100644
index 000000000..1883a9f83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_extend.fixed
@@ -0,0 +1,32 @@
+// run-rustfix
+
+#[derive(Copy, Clone)]
+struct HasChars;
+
+impl HasChars {
+ fn chars(self) -> std::str::Chars<'static> {
+ "HasChars".chars()
+ }
+}
+
+fn main() {
+ let abc = "abc";
+ let def = String::from("def");
+ let mut s = String::new();
+
+ s.push_str(abc);
+ s.push_str(abc);
+
+ s.push_str("abc");
+ s.push_str("abc");
+
+ s.push_str(&def);
+ s.push_str(&def);
+
+ s.extend(abc.chars().skip(1));
+ s.extend("abc".chars().skip(1));
+ s.extend(['a', 'b', 'c'].iter());
+
+ let f = HasChars;
+ s.extend(f.chars());
+}
diff --git a/src/tools/clippy/tests/ui/string_extend.rs b/src/tools/clippy/tests/ui/string_extend.rs
new file mode 100644
index 000000000..07d0baa1b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_extend.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+
+#[derive(Copy, Clone)]
+struct HasChars;
+
+impl HasChars {
+ fn chars(self) -> std::str::Chars<'static> {
+ "HasChars".chars()
+ }
+}
+
+fn main() {
+ let abc = "abc";
+ let def = String::from("def");
+ let mut s = String::new();
+
+ s.push_str(abc);
+ s.extend(abc.chars());
+
+ s.push_str("abc");
+ s.extend("abc".chars());
+
+ s.push_str(&def);
+ s.extend(def.chars());
+
+ s.extend(abc.chars().skip(1));
+ s.extend("abc".chars().skip(1));
+ s.extend(['a', 'b', 'c'].iter());
+
+ let f = HasChars;
+ s.extend(f.chars());
+}
diff --git a/src/tools/clippy/tests/ui/string_extend.stderr b/src/tools/clippy/tests/ui/string_extend.stderr
new file mode 100644
index 000000000..6af8c9e16
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_extend.stderr
@@ -0,0 +1,22 @@
+error: calling `.extend(_.chars())`
+ --> $DIR/string_extend.rs:18:5
+ |
+LL | s.extend(abc.chars());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)`
+ |
+ = note: `-D clippy::string-extend-chars` implied by `-D warnings`
+
+error: calling `.extend(_.chars())`
+ --> $DIR/string_extend.rs:21:5
+ |
+LL | s.extend("abc".chars());
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")`
+
+error: calling `.extend(_.chars())`
+ --> $DIR/string_extend.rs:24:5
+ |
+LL | s.extend(def.chars());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed
new file mode 100644
index 000000000..6e665cdd5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed
@@ -0,0 +1,6 @@
+// run-rustfix
+#![warn(clippy::string_from_utf8_as_bytes)]
+
+fn main() {
+ let _ = Some(&"Hello World!"[6..11]);
+}
diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs
new file mode 100644
index 000000000..670d206d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs
@@ -0,0 +1,6 @@
+// run-rustfix
+#![warn(clippy::string_from_utf8_as_bytes)]
+
+fn main() {
+ let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]);
+}
diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr
new file mode 100644
index 000000000..bf5e5d33e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr
@@ -0,0 +1,10 @@
+error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary
+ --> $DIR/string_from_utf8_as_bytes.rs:5:13
+ |
+LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])`
+ |
+ = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
new file mode 100644
index 000000000..df2256e4f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+#![warn(clippy::string_lit_as_bytes)]
+
+fn str_lit_as_bytes() {
+ let bs = b"hello there";
+
+ let bs = br###"raw string with 3# plus " ""###;
+
+ let bs = b"lit to string".to_vec();
+ let bs = b"lit to owned".to_vec();
+
+ // no warning, because these cannot be written as byte string literals:
+ let ubs = "☃".as_bytes();
+ let ubs = "hello there! this is a very long string".as_bytes();
+
+ let ubs = "☃".to_string().into_bytes();
+ let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
+ let strify = stringify!(foobar).as_bytes();
+
+ let current_version = env!("CARGO_PKG_VERSION").as_bytes();
+
+ let includestr = include_bytes!("string_lit_as_bytes.rs");
+
+ let _ = b"string with newline\t\n";
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
new file mode 100644
index 000000000..c6bf8f732
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+#![warn(clippy::string_lit_as_bytes)]
+
+fn str_lit_as_bytes() {
+ let bs = "hello there".as_bytes();
+
+ let bs = r###"raw string with 3# plus " ""###.as_bytes();
+
+ let bs = "lit to string".to_string().into_bytes();
+ let bs = "lit to owned".to_owned().into_bytes();
+
+ // no warning, because these cannot be written as byte string literals:
+ let ubs = "☃".as_bytes();
+ let ubs = "hello there! this is a very long string".as_bytes();
+
+ let ubs = "☃".to_string().into_bytes();
+ let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
+ let strify = stringify!(foobar).as_bytes();
+
+ let current_version = env!("CARGO_PKG_VERSION").as_bytes();
+
+ let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
+
+ let _ = "string with newline\t\n".as_bytes();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
new file mode 100644
index 000000000..f47d6161c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
@@ -0,0 +1,40 @@
+error: calling `as_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:7:14
+ |
+LL | let bs = "hello there".as_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"`
+ |
+ = note: `-D clippy::string-lit-as-bytes` implied by `-D warnings`
+
+error: calling `as_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:9:14
+ |
+LL | let bs = r###"raw string with 3# plus " ""###.as_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###`
+
+error: calling `into_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:11:14
+ |
+LL | let bs = "lit to string".to_string().into_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()`
+
+error: calling `into_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:12:14
+ |
+LL | let bs = "lit to owned".to_owned().into_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()`
+
+error: calling `as_bytes()` on `include_str!(..)`
+ --> $DIR/string_lit_as_bytes.rs:25:22
+ |
+LL | let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")`
+
+error: calling `as_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:27:13
+ |
+LL | let _ = "string with newline/t/n".as_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_slice.rs b/src/tools/clippy/tests/ui/string_slice.rs
new file mode 100644
index 000000000..be4dfc881
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_slice.rs
@@ -0,0 +1,10 @@
+#[warn(clippy::string_slice)]
+#[allow(clippy::no_effect)]
+
+fn main() {
+ &"Ölkanne"[1..];
+ let m = "Mötörhead";
+ &m[2..5];
+ let s = String::from(m);
+ &s[0..2];
+}
diff --git a/src/tools/clippy/tests/ui/string_slice.stderr b/src/tools/clippy/tests/ui/string_slice.stderr
new file mode 100644
index 000000000..55040bf5d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_slice.stderr
@@ -0,0 +1,22 @@
+error: indexing into a string may panic if the index is within a UTF-8 character
+ --> $DIR/string_slice.rs:5:6
+ |
+LL | &"Ölkanne"[1..];
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::string-slice` implied by `-D warnings`
+
+error: indexing into a string may panic if the index is within a UTF-8 character
+ --> $DIR/string_slice.rs:7:6
+ |
+LL | &m[2..5];
+ | ^^^^^^^
+
+error: indexing into a string may panic if the index is within a UTF-8 character
+ --> $DIR/string_slice.rs:9:6
+ |
+LL | &s[0..2];
+ | ^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/string_to_string.rs b/src/tools/clippy/tests/ui/string_to_string.rs
new file mode 100644
index 000000000..4c66855f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_to_string.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::string_to_string)]
+#![allow(clippy::redundant_clone)]
+
+fn main() {
+ let mut message = String::from("Hello");
+ let mut v = message.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/string_to_string.stderr b/src/tools/clippy/tests/ui/string_to_string.stderr
new file mode 100644
index 000000000..1ebd17999
--- /dev/null
+++ b/src/tools/clippy/tests/ui/string_to_string.stderr
@@ -0,0 +1,11 @@
+error: `to_string()` called on a `String`
+ --> $DIR/string_to_string.rs:6:17
+ |
+LL | let mut v = message.to_string();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::string-to-string` implied by `-D warnings`
+ = help: consider using `.clone()`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
new file mode 100644
index 000000000..947a59bcc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+ // CString
+ let cstring = CString::new("foo").expect("CString::new failed");
+ let _ = cstring.as_bytes().len();
+
+ // CStr
+ let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+ let _ = cstr.to_bytes().len();
+
+ let _ = cstr.to_bytes().len();
+
+ let pcstr: *const &CStr = &cstr;
+ let _ = unsafe { (*pcstr).to_bytes().len() };
+
+ unsafe fn unsafe_identity<T>(x: T) -> T {
+ x
+ }
+ let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
+ let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
+
+ let f: unsafe fn(_) -> _ = unsafe_identity;
+ let _ = unsafe { f(cstr).to_bytes().len() };
+}
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
new file mode 100644
index 000000000..1237f1ab0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+ // CString
+ let cstring = CString::new("foo").expect("CString::new failed");
+ let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+
+ // CStr
+ let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+ let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+
+ let _ = unsafe { strlen(cstr.as_ptr()) };
+
+ let pcstr: *const &CStr = &cstr;
+ let _ = unsafe { strlen((*pcstr).as_ptr()) };
+
+ unsafe fn unsafe_identity<T>(x: T) -> T {
+ x
+ }
+ let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+ let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+
+ let f: unsafe fn(_) -> _ = unsafe_identity;
+ let _ = unsafe { strlen(f(cstr).as_ptr()) };
+}
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr b/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
new file mode 100644
index 000000000..296268a5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
@@ -0,0 +1,46 @@
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:15:13
+ |
+LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()`
+ |
+ = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:19:13
+ |
+LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:21:13
+ |
+LL | let _ = unsafe { strlen(cstr.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:24:22
+ |
+LL | let _ = unsafe { strlen((*pcstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:29:22
+ |
+LL | let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:30:13
+ |
+LL | let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:33:22
+ |
+LL | let _ = unsafe { strlen(f(cstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.rs b/src/tools/clippy/tests/ui/struct_excessive_bools.rs
new file mode 100644
index 000000000..ce4fe830a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/struct_excessive_bools.rs
@@ -0,0 +1,44 @@
+#![warn(clippy::struct_excessive_bools)]
+
+macro_rules! foo {
+ () => {
+ struct MacroFoo {
+ a: bool,
+ b: bool,
+ c: bool,
+ d: bool,
+ }
+ };
+}
+
+foo!();
+
+struct Foo {
+ a: bool,
+ b: bool,
+ c: bool,
+}
+
+struct BadFoo {
+ a: bool,
+ b: bool,
+ c: bool,
+ d: bool,
+}
+
+#[repr(C)]
+struct Bar {
+ a: bool,
+ b: bool,
+ c: bool,
+ d: bool,
+}
+
+fn main() {
+ struct FooFoo {
+ a: bool,
+ b: bool,
+ c: bool,
+ d: bool,
+ }
+}
diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.stderr b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr
new file mode 100644
index 000000000..2941bf298
--- /dev/null
+++ b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr
@@ -0,0 +1,29 @@
+error: more than 3 bools in a struct
+ --> $DIR/struct_excessive_bools.rs:22:1
+ |
+LL | / struct BadFoo {
+LL | | a: bool,
+LL | | b: bool,
+LL | | c: bool,
+LL | | d: bool,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
+ = help: consider using a state machine or refactoring bools into two-variant enums
+
+error: more than 3 bools in a struct
+ --> $DIR/struct_excessive_bools.rs:38:5
+ |
+LL | / struct FooFoo {
+LL | | a: bool,
+LL | | b: bool,
+LL | | c: bool,
+LL | | d: bool,
+LL | | }
+ | |_____^
+ |
+ = help: consider using a state machine or refactoring bools into two-variant enums
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs
new file mode 100644
index 000000000..ae253a048
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs
@@ -0,0 +1,170 @@
+#![warn(clippy::suspicious_arithmetic_impl)]
+use std::ops::{
+ Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub,
+};
+
+#[derive(Copy, Clone)]
+struct Foo(u32);
+
+impl Add for Foo {
+ type Output = Foo;
+
+ fn add(self, other: Self) -> Self {
+ Foo(self.0 - other.0)
+ }
+}
+
+impl AddAssign for Foo {
+ fn add_assign(&mut self, other: Foo) {
+ *self = *self - other;
+ }
+}
+
+impl BitOrAssign for Foo {
+ fn bitor_assign(&mut self, other: Foo) {
+ let idx = other.0;
+ self.0 |= 1 << idx; // OK: BinOpKind::Shl part of AssignOp as child node
+ }
+}
+
+impl MulAssign for Foo {
+ fn mul_assign(&mut self, other: Foo) {
+ self.0 /= other.0;
+ }
+}
+
+impl DivAssign for Foo {
+ fn div_assign(&mut self, other: Foo) {
+ self.0 /= other.0; // OK: BinOpKind::Div == DivAssign
+ }
+}
+
+impl Mul for Foo {
+ type Output = Foo;
+
+ fn mul(self, other: Foo) -> Foo {
+ Foo(self.0 * other.0 % 42) // OK: BinOpKind::Rem part of BiExpr as parent node
+ }
+}
+
+impl Sub for Foo {
+ type Output = Foo;
+
+ fn sub(self, other: Self) -> Self {
+ Foo(self.0 * other.0 - 42) // OK: BinOpKind::Mul part of BiExpr as child node
+ }
+}
+
+impl Div for Foo {
+ type Output = Foo;
+
+ fn div(self, other: Self) -> Self {
+ Foo(do_nothing(self.0 + other.0) / 42) // OK: BinOpKind::Add part of BiExpr as child node
+ }
+}
+
+impl Rem for Foo {
+ type Output = Foo;
+
+ fn rem(self, other: Self) -> Self {
+ Foo(self.0 / other.0)
+ }
+}
+
+impl BitAnd for Foo {
+ type Output = Foo;
+
+ fn bitand(self, other: Self) -> Self {
+ Foo(self.0 | other.0)
+ }
+}
+
+impl BitOr for Foo {
+ type Output = Foo;
+
+ fn bitor(self, other: Self) -> Self {
+ Foo(self.0 ^ other.0)
+ }
+}
+
+impl BitXor for Foo {
+ type Output = Foo;
+
+ fn bitxor(self, other: Self) -> Self {
+ Foo(self.0 & other.0)
+ }
+}
+
+impl Shl for Foo {
+ type Output = Foo;
+
+ fn shl(self, other: Self) -> Self {
+ Foo(self.0 >> other.0)
+ }
+}
+
+impl Shr for Foo {
+ type Output = Foo;
+
+ fn shr(self, other: Self) -> Self {
+ Foo(self.0 << other.0)
+ }
+}
+
+struct Bar(i32);
+
+impl Add for Bar {
+ type Output = Bar;
+
+ fn add(self, other: Self) -> Self {
+ Bar(self.0 & !other.0) // OK: Not part of BiExpr as child node
+ }
+}
+
+impl Sub for Bar {
+ type Output = Bar;
+
+ fn sub(self, other: Self) -> Self {
+ if self.0 <= other.0 {
+ Bar(-(self.0 & other.0)) // OK: Neg part of BiExpr as parent node
+ } else {
+ Bar(0)
+ }
+ }
+}
+
+fn main() {}
+
+fn do_nothing(x: u32) -> u32 {
+ x
+}
+
+struct MultipleBinops(u32);
+
+impl Add for MultipleBinops {
+ type Output = MultipleBinops;
+
+ // OK: multiple Binops in `add` impl
+ fn add(self, other: Self) -> Self::Output {
+ let mut result = self.0 + other.0;
+ if result >= u32::max_value() {
+ result -= u32::max_value();
+ }
+ MultipleBinops(result)
+ }
+}
+
+impl Mul for MultipleBinops {
+ type Output = MultipleBinops;
+
+ // OK: multiple Binops in `mul` impl
+ fn mul(self, other: Self) -> Self::Output {
+ let mut result: u32 = 0;
+ let size = std::cmp::max(self.0, other.0) as usize;
+ let mut v = vec![0; size + 1];
+ for i in 0..size + 1 {
+ result *= i as u32;
+ }
+ MultipleBinops(result)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
new file mode 100644
index 000000000..ced130587
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
@@ -0,0 +1,60 @@
+error: suspicious use of `-` in `Add` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:13:20
+ |
+LL | Foo(self.0 - other.0)
+ | ^
+ |
+ = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
+
+error: suspicious use of `-` in `AddAssign` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:19:23
+ |
+LL | *self = *self - other;
+ | ^
+ |
+ = note: `-D clippy::suspicious-op-assign-impl` implied by `-D warnings`
+
+error: suspicious use of `/` in `MulAssign` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:32:16
+ |
+LL | self.0 /= other.0;
+ | ^^
+
+error: suspicious use of `/` in `Rem` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:70:20
+ |
+LL | Foo(self.0 / other.0)
+ | ^
+
+error: suspicious use of `|` in `BitAnd` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:78:20
+ |
+LL | Foo(self.0 | other.0)
+ | ^
+
+error: suspicious use of `^` in `BitOr` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:86:20
+ |
+LL | Foo(self.0 ^ other.0)
+ | ^
+
+error: suspicious use of `&` in `BitXor` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:94:20
+ |
+LL | Foo(self.0 & other.0)
+ | ^
+
+error: suspicious use of `>>` in `Shl` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:102:20
+ |
+LL | Foo(self.0 >> other.0)
+ | ^^
+
+error: suspicious use of `<<` in `Shr` impl
+ --> $DIR/suspicious_arithmetic_impl.rs:110:20
+ |
+LL | Foo(self.0 << other.0)
+ | ^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
new file mode 100644
index 000000000..21753e5dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
@@ -0,0 +1,115 @@
+// aux-build:proc_macro_suspicious_else_formatting.rs
+
+#![warn(clippy::suspicious_else_formatting)]
+#![allow(clippy::if_same_then_else, clippy::let_unit_value)]
+
+extern crate proc_macro_suspicious_else_formatting;
+use proc_macro_suspicious_else_formatting::DeriveBadSpan;
+
+fn foo() -> bool {
+ true
+}
+
+#[rustfmt::skip]
+fn main() {
+ // weird `else` formatting:
+ if foo() {
+ } {
+ }
+
+ if foo() {
+ } if foo() {
+ }
+
+ let _ = { // if as the last expression
+ let _ = 0;
+
+ if foo() {
+ } if foo() {
+ }
+ else {
+ }
+ };
+
+ let _ = { // if in the middle of a block
+ if foo() {
+ } if foo() {
+ }
+ else {
+ }
+
+ let _ = 0;
+ };
+
+ if foo() {
+ } else
+ {
+ }
+
+ // This is fine, though weird. Allman style braces on the else.
+ if foo() {
+ }
+ else
+ {
+ }
+
+ if foo() {
+ } else
+ if foo() { // the span of the above error should continue here
+ }
+
+ if foo() {
+ }
+ else
+ if foo() { // the span of the above error should continue here
+ }
+
+ // those are ok:
+ if foo() {
+ }
+ {
+ }
+
+ if foo() {
+ } else {
+ }
+
+ if foo() {
+ }
+ else {
+ }
+
+ if foo() {
+ }
+ if foo() {
+ }
+
+ // Almost Allman style braces. Lint these.
+ if foo() {
+ }
+
+ else
+ {
+
+ }
+
+ if foo() {
+ }
+ else
+
+ {
+
+ }
+
+ // #3864 - Allman style braces
+ if foo()
+ {
+ }
+ else
+ {
+ }
+}
+
+// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
+#[derive(DeriveBadSpan)]
+struct _Foo(u32, u32);
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
new file mode 100644
index 000000000..ee68eb5a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
@@ -0,0 +1,90 @@
+error: this looks like an `else {..}` but the `else` is missing
+ --> $DIR/suspicious_else_formatting.rs:17:6
+ |
+LL | } {
+ | ^
+ |
+ = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
+ = note: to remove this lint, add the missing `else` or add a new line before the next block
+
+error: this looks like an `else if` but the `else` is missing
+ --> $DIR/suspicious_else_formatting.rs:21:6
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
+ --> $DIR/suspicious_else_formatting.rs:28:10
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
+ --> $DIR/suspicious_else_formatting.rs:36:10
+ |
+LL | } if foo() {
+ | ^
+ |
+ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this is an `else {..}` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:45:6
+ |
+LL | } else
+ | ______^
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else if` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:57:6
+ |
+LL | } else
+ | ______^
+LL | | if foo() { // the span of the above error should continue here
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: this is an `else if` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:62:6
+ |
+LL | }
+ | ______^
+LL | | else
+LL | | if foo() { // the span of the above error should continue here
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: this is an `else {..}` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:89:6
+ |
+LL | }
+ | ______^
+LL | |
+LL | | else
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
+ --> $DIR/suspicious_else_formatting.rs:97:6
+ |
+LL | }
+ | ______^
+LL | | else
+LL | |
+LL | | {
+ | |____^
+ |
+ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs
new file mode 100644
index 000000000..3a2a10cf0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_map.rs
@@ -0,0 +1,32 @@
+#![warn(clippy::suspicious_map)]
+
+fn main() {
+ let _ = (0..3).map(|x| x + 2).count();
+
+ let f = |x| x + 1;
+ let _ = (0..3).map(f).count();
+}
+
+fn negative() {
+ // closure with side effects
+ let mut sum = 0;
+ let _ = (0..3).map(|x| sum += x).count();
+
+ // closure variable with side effects
+ let ext_closure = |x| sum += x;
+ let _ = (0..3).map(ext_closure).count();
+
+ // closure that returns unit
+ let _ = (0..3)
+ .map(|x| {
+ // do nothing
+ })
+ .count();
+
+ // external function
+ let _ = (0..3).map(do_something).count();
+}
+
+fn do_something<T>(t: T) -> String {
+ unimplemented!()
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr
new file mode 100644
index 000000000..3ffcd1a90
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_map.stderr
@@ -0,0 +1,19 @@
+error: this call to `map()` won't have an effect on the call to `count()`
+ --> $DIR/suspicious_map.rs:4:13
+ |
+LL | let _ = (0..3).map(|x| x + 2).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::suspicious-map` implied by `-D warnings`
+ = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
+
+error: this call to `map()` won't have an effect on the call to `count()`
+ --> $DIR/suspicious_map.rs:7:13
+ |
+LL | let _ = (0..3).map(f).count();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_operation_groupings.fixed b/src/tools/clippy/tests/ui/suspicious_operation_groupings.fixed
new file mode 100644
index 000000000..ede8a39fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_operation_groupings.fixed
@@ -0,0 +1,209 @@
+// run-rustfix
+#![warn(clippy::suspicious_operation_groupings)]
+#![allow(dead_code, unused_parens, clippy::eq_op)]
+
+struct Vec3 {
+ x: f64,
+ y: f64,
+ z: f64,
+}
+
+impl Eq for Vec3 {}
+
+impl PartialEq for Vec3 {
+ fn eq(&self, other: &Self) -> bool {
+ // This should trigger the lint because `self.x` is compared to `other.y`
+ self.x == other.x && self.y == other.y && self.z == other.z
+ }
+}
+
+struct S {
+ a: i32,
+ b: i32,
+ c: i32,
+ d: i32,
+}
+
+fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
+ // There's no `s1.b`
+ s1.a < s2.a && s1.b < s2.b
+}
+
+struct SaOnly {
+ a: i32,
+}
+
+impl S {
+ fn a(&self) -> i32 {
+ 0
+ }
+}
+
+fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool {
+ // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
+ // `s2.b` since that is invalid.
+ s1.a < s2.a && s1.a() < s1.b
+}
+
+fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
+ macro_rules! s1 {
+ () => {
+ S {
+ a: 1,
+ b: 1,
+ c: 1,
+ d: 1,
+ }
+ };
+ }
+
+ // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
+ // `s2.b` since that is invalid.
+ s1.a < s2.a && s1!().a < s1.b
+}
+
+fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool {
+ // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid
+ s1.a < s2.a && s1.b < s1.b
+}
+
+fn permissable(s1: &S, s2: &S) -> bool {
+ // Something like this seems like it might actually be what is desired.
+ s1.a == s2.b
+}
+
+fn non_boolean_operators(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d
+}
+
+fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.a`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) {
+ (
+ s1.b * s2.c - s1.c * s2.b,
+ s1.c * s2.a - s1.a * s2.c,
+ s1.a * s2.b - s1.b * s2.a,
+ )
+}
+
+fn outer_parens_simple(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ (s1.a * s2.a + s1.b * s2.b)
+}
+
+fn outer_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d)
+}
+
+fn inner_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)
+}
+
+fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d))
+}
+
+fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.c) + (s1.d * s2.d)))
+}
+
+fn all_parens_left_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c)) + (s1.d * s2.d))
+}
+
+fn all_parens_right_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)))
+}
+
+fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ (s1.a * s2.a + s1.b * s2.b) / 2
+}
+
+fn inside_function_call(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ i32::swap_bytes(s1.a * s2.a + s1.b * s2.b)
+}
+
+fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool {
+ // There's no `s1.c`
+ s1.a > 0 && s1.b > 0 && s1.c == s2.c && s1.d == s2.d
+}
+
+fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool {
+ // There's no `s1.c`
+ s1.a > 0 && s1.c == s2.c && s1.b > 0 && s1.d == s2.d
+}
+
+struct Nested {
+ inner: ((i32,), (i32,), (i32,)),
+}
+
+fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool {
+ // There's no `n2.inner.2.0`
+ (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+}
+
+// `eq_op` should catch this one.
+fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool {
+ // There's no `n2.inner.0.0`
+ (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+}
+
+fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool {
+ if strict {
+ s1.a < s2.a && s1.b < s2.b
+ } else {
+ // There's no `s1.b` in this subexpression
+ s1.a <= s2.a && s1.b <= s2.b
+ }
+}
+
+fn inside_an_if_statement(s1: &mut S, s2: &S) {
+ // There's no `s1.b`
+ if s1.a < s2.a && s1.b < s2.b {
+ s1.c = s2.c;
+ }
+}
+
+fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.c) + -(-s1.d * -s2.d)))
+}
+
+fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ -(if -s1.a < -s2.a && -s1.b < -s2.b { s1.c } else { s2.a })
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_operation_groupings.rs b/src/tools/clippy/tests/ui/suspicious_operation_groupings.rs
new file mode 100644
index 000000000..26ce97bb3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_operation_groupings.rs
@@ -0,0 +1,209 @@
+// run-rustfix
+#![warn(clippy::suspicious_operation_groupings)]
+#![allow(dead_code, unused_parens, clippy::eq_op)]
+
+struct Vec3 {
+ x: f64,
+ y: f64,
+ z: f64,
+}
+
+impl Eq for Vec3 {}
+
+impl PartialEq for Vec3 {
+ fn eq(&self, other: &Self) -> bool {
+ // This should trigger the lint because `self.x` is compared to `other.y`
+ self.x == other.y && self.y == other.y && self.z == other.z
+ }
+}
+
+struct S {
+ a: i32,
+ b: i32,
+ c: i32,
+ d: i32,
+}
+
+fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
+ // There's no `s1.b`
+ s1.a < s2.a && s1.a < s2.b
+}
+
+struct SaOnly {
+ a: i32,
+}
+
+impl S {
+ fn a(&self) -> i32 {
+ 0
+ }
+}
+
+fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool {
+ // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
+ // `s2.b` since that is invalid.
+ s1.a < s2.a && s1.a() < s1.b
+}
+
+fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
+ macro_rules! s1 {
+ () => {
+ S {
+ a: 1,
+ b: 1,
+ c: 1,
+ d: 1,
+ }
+ };
+ }
+
+ // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
+ // `s2.b` since that is invalid.
+ s1.a < s2.a && s1!().a < s1.b
+}
+
+fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool {
+ // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid
+ s1.a < s2.a && s1.b < s1.b
+}
+
+fn permissable(s1: &S, s2: &S) -> bool {
+ // Something like this seems like it might actually be what is desired.
+ s1.a == s2.b
+}
+
+fn non_boolean_operators(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d
+}
+
+fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ s1.a * s2.a + s2.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ s1.a * s2.a + s1.b * s1.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.a`
+ s1.a * s1.a + s1.b * s2.b + s1.c * s2.c
+}
+
+fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ s1.a * s2.a + s1.b * s2.b + s1.c * s1.c
+}
+
+fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) {
+ (
+ s1.b * s2.c - s1.c * s2.b,
+ s1.c * s2.a - s1.a * s2.c,
+ s1.a * s2.b - s1.b * s2.a,
+ )
+}
+
+fn outer_parens_simple(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.b`
+ (s1.a * s2.a + s1.b * s1.b)
+}
+
+fn outer_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d)
+}
+
+fn inner_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)
+}
+
+fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))
+}
+
+fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
+}
+
+fn all_parens_left_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d))
+}
+
+fn all_parens_right_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)))
+}
+
+fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ (s1.a * s2.a + s2.b * s2.b) / 2
+}
+
+fn inside_function_call(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ i32::swap_bytes(s1.a * s2.a + s2.b * s2.b)
+}
+
+fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool {
+ // There's no `s1.c`
+ s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d
+}
+
+fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool {
+ // There's no `s1.c`
+ s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d
+}
+
+struct Nested {
+ inner: ((i32,), (i32,), (i32,)),
+}
+
+fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool {
+ // There's no `n2.inner.2.0`
+ (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0
+}
+
+// `eq_op` should catch this one.
+fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool {
+ // There's no `n2.inner.0.0`
+ (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
+}
+
+fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool {
+ if strict {
+ s1.a < s2.a && s1.b < s2.b
+ } else {
+ // There's no `s1.b` in this subexpression
+ s1.a <= s2.a && s1.a <= s2.b
+ }
+}
+
+fn inside_an_if_statement(s1: &mut S, s2: &S) {
+ // There's no `s1.b`
+ if s1.a < s2.a && s1.a < s2.b {
+ s1.c = s2.c;
+ }
+}
+
+fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 {
+ // There's no `s2.c`
+ -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d)))
+}
+
+fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 {
+ // There's no `s1.b`
+ -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a })
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr b/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr
new file mode 100644
index 000000000..29f229245
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr
@@ -0,0 +1,160 @@
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:16:9
+ |
+LL | self.x == other.y && self.y == other.y && self.z == other.z
+ | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x`
+ |
+ = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:29:20
+ |
+LL | s1.a < s2.a && s1.a < s2.b
+ | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:77:33
+ |
+LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:82:19
+ |
+LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:82:19
+ |
+LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:87:19
+ |
+LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:92:19
+ |
+LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:97:5
+ |
+LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:102:33
+ |
+LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:115:20
+ |
+LL | (s1.a * s2.a + s1.b * s1.b)
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:120:34
+ |
+LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d)
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:125:38
+ |
+LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:130:39
+ |
+LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:135:42
+ |
+LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:135:42
+ |
+LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:140:40
+ |
+LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d))
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:145:40
+ |
+LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)))
+ | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:150:20
+ |
+LL | (s1.a * s2.a + s2.b * s2.b) / 2
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:155:35
+ |
+LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b)
+ | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:160:29
+ |
+LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d
+ | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:165:17
+ |
+LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d
+ | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:174:77
+ |
+LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:188:25
+ |
+LL | s1.a <= s2.a && s1.a <= s2.b
+ | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:194:23
+ |
+LL | if s1.a < s2.a && s1.a < s2.b {
+ | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:201:48
+ |
+LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d)))
+ | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c`
+
+error: this sequence of operators looks suspiciously like a bug
+ --> $DIR/suspicious_operation_groupings.rs:206:27
+ |
+LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a })
+ | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b`
+
+error: aborting due to 26 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_splitn.rs b/src/tools/clippy/tests/ui/suspicious_splitn.rs
new file mode 100644
index 000000000..528f2ddcc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_splitn.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::suspicious_splitn)]
+#![allow(clippy::needless_splitn)]
+
+fn main() {
+ let _ = "a,b,c".splitn(3, ',');
+ let _ = [0, 1, 2, 1, 3].splitn(3, |&x| x == 1);
+ let _ = "".splitn(0, ',');
+ let _ = [].splitn(0, |&x: &u32| x == 1);
+
+ let _ = "a,b".splitn(0, ',');
+ let _ = "a,b".rsplitn(0, ',');
+ let _ = "a,b".splitn(1, ',');
+ let _ = [0, 1, 2].splitn(0, |&x| x == 1);
+ let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
+ let _ = [0, 1, 2].splitn(1, |&x| x == 1);
+ let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
+
+ const X: usize = 0;
+ let _ = "a,b".splitn(X + 1, ',');
+ let _ = "a,b".splitn(X, ',');
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_splitn.stderr b/src/tools/clippy/tests/ui/suspicious_splitn.stderr
new file mode 100644
index 000000000..3bcd681fa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_splitn.stderr
@@ -0,0 +1,75 @@
+error: `splitn` called with `0` splits
+ --> $DIR/suspicious_splitn.rs:10:13
+ |
+LL | let _ = "a,b".splitn(0, ',');
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::suspicious-splitn` implied by `-D warnings`
+ = note: the resulting iterator will always return `None`
+
+error: `rsplitn` called with `0` splits
+ --> $DIR/suspicious_splitn.rs:11:13
+ |
+LL | let _ = "a,b".rsplitn(0, ',');
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return `None`
+
+error: `splitn` called with `1` split
+ --> $DIR/suspicious_splitn.rs:12:13
+ |
+LL | let _ = "a,b".splitn(1, ',');
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return the entire string followed by `None`
+
+error: `splitn` called with `0` splits
+ --> $DIR/suspicious_splitn.rs:13:13
+ |
+LL | let _ = [0, 1, 2].splitn(0, |&x| x == 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return `None`
+
+error: `splitn_mut` called with `0` splits
+ --> $DIR/suspicious_splitn.rs:14:13
+ |
+LL | let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return `None`
+
+error: `splitn` called with `1` split
+ --> $DIR/suspicious_splitn.rs:15:13
+ |
+LL | let _ = [0, 1, 2].splitn(1, |&x| x == 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return the entire slice followed by `None`
+
+error: `rsplitn_mut` called with `1` split
+ --> $DIR/suspicious_splitn.rs:16:13
+ |
+LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return the entire slice followed by `None`
+
+error: `splitn` called with `1` split
+ --> $DIR/suspicious_splitn.rs:19:13
+ |
+LL | let _ = "a,b".splitn(X + 1, ',');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return the entire string followed by `None`
+
+error: `splitn` called with `0` splits
+ --> $DIR/suspicious_splitn.rs:20:13
+ |
+LL | let _ = "a,b".splitn(X, ',');
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the resulting iterator will always return `None`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
new file mode 100644
index 000000000..9564e373c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::suspicious_unary_op_formatting)]
+
+#[rustfmt::skip]
+fn main() {
+ // weird binary operator formatting:
+ let a = 42;
+
+ if a >- 30 {}
+ if a >=- 30 {}
+
+ let b = true;
+ let c = false;
+
+ if b &&! c {}
+
+ if a >- 30 {}
+
+ // those are ok:
+ if a >-30 {}
+ if a < -30 {}
+ if b && !c {}
+ if a > - 30 {}
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
new file mode 100644
index 000000000..581527dcf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
@@ -0,0 +1,35 @@
+error: by not having a space between `>` and `-` it looks like `>-` is a single operator
+ --> $DIR/suspicious_unary_op_formatting.rs:8:9
+ |
+LL | if a >- 30 {}
+ | ^^^^
+ |
+ = note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings`
+ = help: put a space between `>` and `-` and remove the space after `-`
+
+error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator
+ --> $DIR/suspicious_unary_op_formatting.rs:9:9
+ |
+LL | if a >=- 30 {}
+ | ^^^^^
+ |
+ = help: put a space between `>=` and `-` and remove the space after `-`
+
+error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator
+ --> $DIR/suspicious_unary_op_formatting.rs:14:9
+ |
+LL | if b &&! c {}
+ | ^^^^^
+ |
+ = help: put a space between `&&` and `!` and remove the space after `!`
+
+error: by not having a space between `>` and `-` it looks like `>-` is a single operator
+ --> $DIR/suspicious_unary_op_formatting.rs:16:9
+ |
+LL | if a >- 30 {}
+ | ^^^^^^
+ |
+ = help: put a space between `>` and `-` and remove the space after `-`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
new file mode 100644
index 000000000..3329efbd4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -0,0 +1,157 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::no_effect,
+ clippy::redundant_clone,
+ redundant_semicolons,
+ dead_code,
+ unused_assignments
+)]
+
+struct Foo(u32);
+
+#[derive(Clone)]
+struct Bar {
+ a: u32,
+ b: u32,
+}
+
+fn field() {
+ let mut bar = Bar { a: 1, b: 2 };
+
+ std::mem::swap(&mut bar.a, &mut bar.b);
+
+ let mut baz = vec![bar.clone(), bar.clone()];
+ let temp = baz[0].a;
+ baz[0].a = baz[1].a;
+ baz[1].a = temp;
+}
+
+fn array() {
+ let mut foo = [1, 2];
+ foo.swap(0, 1);
+
+ foo.swap(0, 1);
+}
+
+fn slice() {
+ let foo = &mut [1, 2];
+ foo.swap(0, 1);
+
+ foo.swap(0, 1);
+}
+
+fn unswappable_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ let temp = foo[0][1];
+ foo[0][1] = foo[1][0];
+ foo[1][0] = temp;
+
+ // swap(foo[0][1], foo[1][0]) would fail
+ // this could use split_at_mut and mem::swap, but that is not much simpler.
+}
+
+fn vec() {
+ let mut foo = vec![1, 2];
+ foo.swap(0, 1);
+
+ foo.swap(0, 1);
+}
+
+fn xor_swap_locals() {
+ // This is an xor-based swap of local variables.
+ let mut a = 0;
+ let mut b = 1;
+ std::mem::swap(&mut a, &mut b)
+}
+
+fn xor_field_swap() {
+ // This is an xor-based swap of fields in a struct.
+ let mut bar = Bar { a: 0, b: 1 };
+ std::mem::swap(&mut bar.a, &mut bar.b)
+}
+
+fn xor_slice_swap() {
+ // This is an xor-based swap of a slice
+ let foo = &mut [1, 2];
+ foo.swap(0, 1)
+}
+
+fn xor_no_swap() {
+ // This is a sequence of xor-assignment statements that doesn't result in a swap.
+ let mut a = 0;
+ let mut b = 1;
+ let mut c = 2;
+ a ^= b;
+ b ^= c;
+ a ^= c;
+ c ^= a;
+}
+
+fn xor_unswappable_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ foo[0][1] ^= foo[1][0];
+ foo[1][0] ^= foo[0][0];
+ foo[0][1] ^= foo[1][0];
+
+ // swap(foo[0][1], foo[1][0]) would fail
+ // this could use split_at_mut and mem::swap, but that is not much simpler.
+}
+
+fn distinct_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ let bar = &mut [vec![1, 2], vec![3, 4]];
+ std::mem::swap(&mut foo[0][1], &mut bar[1][0]);
+}
+
+#[rustfmt::skip]
+fn main() {
+
+ let mut a = 42;
+ let mut b = 1337;
+
+ std::mem::swap(&mut a, &mut b);
+
+ ; std::mem::swap(&mut a, &mut b);
+
+ let mut c = Foo(42);
+
+ std::mem::swap(&mut c.0, &mut a);
+
+ ; std::mem::swap(&mut c.0, &mut a);
+}
+
+fn issue_8154() {
+ struct S1 {
+ x: i32,
+ y: i32,
+ }
+ struct S2(S1);
+ struct S3<'a, 'b>(&'a mut &'b mut S1);
+
+ impl std::ops::Deref for S2 {
+ type Target = S1;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl std::ops::DerefMut for S2 {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
+ let mut s = S2(S1 { x: 0, y: 0 });
+ let t = s.x;
+ s.x = s.y;
+ s.y = t;
+
+ // Accessing through a mutable reference is fine
+ let mut s = S1 { x: 0, y: 0 };
+ let mut s = &mut s;
+ let s = S3(&mut s);
+ std::mem::swap(&mut s.0.x, &mut s.0.y);
+}
diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs
new file mode 100644
index 000000000..8179ac1f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap.rs
@@ -0,0 +1,181 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![allow(
+ clippy::blacklisted_name,
+ clippy::no_effect,
+ clippy::redundant_clone,
+ redundant_semicolons,
+ dead_code,
+ unused_assignments
+)]
+
+struct Foo(u32);
+
+#[derive(Clone)]
+struct Bar {
+ a: u32,
+ b: u32,
+}
+
+fn field() {
+ let mut bar = Bar { a: 1, b: 2 };
+
+ let temp = bar.a;
+ bar.a = bar.b;
+ bar.b = temp;
+
+ let mut baz = vec![bar.clone(), bar.clone()];
+ let temp = baz[0].a;
+ baz[0].a = baz[1].a;
+ baz[1].a = temp;
+}
+
+fn array() {
+ let mut foo = [1, 2];
+ let temp = foo[0];
+ foo[0] = foo[1];
+ foo[1] = temp;
+
+ foo.swap(0, 1);
+}
+
+fn slice() {
+ let foo = &mut [1, 2];
+ let temp = foo[0];
+ foo[0] = foo[1];
+ foo[1] = temp;
+
+ foo.swap(0, 1);
+}
+
+fn unswappable_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ let temp = foo[0][1];
+ foo[0][1] = foo[1][0];
+ foo[1][0] = temp;
+
+ // swap(foo[0][1], foo[1][0]) would fail
+ // this could use split_at_mut and mem::swap, but that is not much simpler.
+}
+
+fn vec() {
+ let mut foo = vec![1, 2];
+ let temp = foo[0];
+ foo[0] = foo[1];
+ foo[1] = temp;
+
+ foo.swap(0, 1);
+}
+
+fn xor_swap_locals() {
+ // This is an xor-based swap of local variables.
+ let mut a = 0;
+ let mut b = 1;
+ a ^= b;
+ b ^= a;
+ a ^= b;
+}
+
+fn xor_field_swap() {
+ // This is an xor-based swap of fields in a struct.
+ let mut bar = Bar { a: 0, b: 1 };
+ bar.a ^= bar.b;
+ bar.b ^= bar.a;
+ bar.a ^= bar.b;
+}
+
+fn xor_slice_swap() {
+ // This is an xor-based swap of a slice
+ let foo = &mut [1, 2];
+ foo[0] ^= foo[1];
+ foo[1] ^= foo[0];
+ foo[0] ^= foo[1];
+}
+
+fn xor_no_swap() {
+ // This is a sequence of xor-assignment statements that doesn't result in a swap.
+ let mut a = 0;
+ let mut b = 1;
+ let mut c = 2;
+ a ^= b;
+ b ^= c;
+ a ^= c;
+ c ^= a;
+}
+
+fn xor_unswappable_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ foo[0][1] ^= foo[1][0];
+ foo[1][0] ^= foo[0][0];
+ foo[0][1] ^= foo[1][0];
+
+ // swap(foo[0][1], foo[1][0]) would fail
+ // this could use split_at_mut and mem::swap, but that is not much simpler.
+}
+
+fn distinct_slice() {
+ let foo = &mut [vec![1, 2], vec![3, 4]];
+ let bar = &mut [vec![1, 2], vec![3, 4]];
+ let temp = foo[0][1];
+ foo[0][1] = bar[1][0];
+ bar[1][0] = temp;
+}
+
+#[rustfmt::skip]
+fn main() {
+
+ let mut a = 42;
+ let mut b = 1337;
+
+ a = b;
+ b = a;
+
+ ; let t = a;
+ a = b;
+ b = t;
+
+ let mut c = Foo(42);
+
+ c.0 = a;
+ a = c.0;
+
+ ; let t = c.0;
+ c.0 = a;
+ a = t;
+}
+
+fn issue_8154() {
+ struct S1 {
+ x: i32,
+ y: i32,
+ }
+ struct S2(S1);
+ struct S3<'a, 'b>(&'a mut &'b mut S1);
+
+ impl std::ops::Deref for S2 {
+ type Target = S1;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl std::ops::DerefMut for S2 {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
+ let mut s = S2(S1 { x: 0, y: 0 });
+ let t = s.x;
+ s.x = s.y;
+ s.y = t;
+
+ // Accessing through a mutable reference is fine
+ let mut s = S1 { x: 0, y: 0 };
+ let mut s = &mut s;
+ let s = S3(&mut s);
+ let t = s.0.x;
+ s.0.x = s.0.y;
+ s.0.y = t;
+}
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
new file mode 100644
index 000000000..2b556b475
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -0,0 +1,122 @@
+error: this looks like you are swapping `bar.a` and `bar.b` manually
+ --> $DIR/swap.rs:24:5
+ |
+LL | / let temp = bar.a;
+LL | | bar.a = bar.b;
+LL | | bar.b = temp;
+ | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+ |
+ = note: `-D clippy::manual-swap` implied by `-D warnings`
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are swapping elements of `foo` manually
+ --> $DIR/swap.rs:36:5
+ |
+LL | / let temp = foo[0];
+LL | | foo[0] = foo[1];
+LL | | foo[1] = temp;
+ | |_________________^ help: try: `foo.swap(0, 1)`
+
+error: this looks like you are swapping elements of `foo` manually
+ --> $DIR/swap.rs:45:5
+ |
+LL | / let temp = foo[0];
+LL | | foo[0] = foo[1];
+LL | | foo[1] = temp;
+ | |_________________^ help: try: `foo.swap(0, 1)`
+
+error: this looks like you are swapping elements of `foo` manually
+ --> $DIR/swap.rs:64:5
+ |
+LL | / let temp = foo[0];
+LL | | foo[0] = foo[1];
+LL | | foo[1] = temp;
+ | |_________________^ help: try: `foo.swap(0, 1)`
+
+error: this looks like you are swapping `a` and `b` manually
+ --> $DIR/swap.rs:75:5
+ |
+LL | / a ^= b;
+LL | | b ^= a;
+LL | | a ^= b;
+ | |___________^ help: try: `std::mem::swap(&mut a, &mut b)`
+
+error: this looks like you are swapping `bar.a` and `bar.b` manually
+ --> $DIR/swap.rs:83:5
+ |
+LL | / bar.a ^= bar.b;
+LL | | bar.b ^= bar.a;
+LL | | bar.a ^= bar.b;
+ | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+
+error: this looks like you are swapping elements of `foo` manually
+ --> $DIR/swap.rs:91:5
+ |
+LL | / foo[0] ^= foo[1];
+LL | | foo[1] ^= foo[0];
+LL | | foo[0] ^= foo[1];
+ | |_____________________^ help: try: `foo.swap(0, 1)`
+
+error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
+ --> $DIR/swap.rs:120:5
+ |
+LL | / let temp = foo[0][1];
+LL | | foo[0][1] = bar[1][0];
+LL | | bar[1][0] = temp;
+ | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])`
+ |
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are swapping `a` and `b` manually
+ --> $DIR/swap.rs:134:7
+ |
+LL | ; let t = a;
+ | _______^
+LL | | a = b;
+LL | | b = t;
+ | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+ |
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are swapping `c.0` and `a` manually
+ --> $DIR/swap.rs:143:7
+ |
+LL | ; let t = c.0;
+ | _______^
+LL | | c.0 = a;
+LL | | a = t;
+ | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+ |
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are trying to swap `a` and `b`
+ --> $DIR/swap.rs:131:5
+ |
+LL | / a = b;
+LL | | b = a;
+ | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+ |
+ = note: `-D clippy::almost-swapped` implied by `-D warnings`
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are trying to swap `c.0` and `a`
+ --> $DIR/swap.rs:140:5
+ |
+LL | / c.0 = a;
+LL | | a = c.0;
+ | |___________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+ |
+ = note: or maybe you should use `std::mem::replace`?
+
+error: this looks like you are swapping `s.0.x` and `s.0.y` manually
+ --> $DIR/swap.rs:178:5
+ |
+LL | / let t = s.0.x;
+LL | | s.0.x = s.0.y;
+LL | | s.0.y = t;
+ | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)`
+ |
+ = note: or maybe you should use `std::mem::replace`?
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/swap_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/swap_ptr_to_ref.fixed
new file mode 100644
index 000000000..596b6ee91
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap_ptr_to_ref.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+
+#![warn(clippy::swap_ptr_to_ref)]
+
+use core::ptr::addr_of_mut;
+
+fn main() {
+ let mut x = 0u32;
+ let y: *mut _ = &mut x;
+ let z: *mut _ = &mut x;
+
+ unsafe {
+ core::ptr::swap(y, z);
+ core::ptr::swap(y, &mut x);
+ core::ptr::swap(&mut x, y);
+ core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x));
+ }
+
+ let y = &mut x;
+ let mut z = 0u32;
+ let z = &mut z;
+
+ core::mem::swap(y, z);
+}
diff --git a/src/tools/clippy/tests/ui/swap_ptr_to_ref.rs b/src/tools/clippy/tests/ui/swap_ptr_to_ref.rs
new file mode 100644
index 000000000..282f57121
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap_ptr_to_ref.rs
@@ -0,0 +1,24 @@
+// run-rustfix
+
+#![warn(clippy::swap_ptr_to_ref)]
+
+use core::ptr::addr_of_mut;
+
+fn main() {
+ let mut x = 0u32;
+ let y: *mut _ = &mut x;
+ let z: *mut _ = &mut x;
+
+ unsafe {
+ core::mem::swap(&mut *y, &mut *z);
+ core::mem::swap(&mut *y, &mut x);
+ core::mem::swap(&mut x, &mut *y);
+ core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x));
+ }
+
+ let y = &mut x;
+ let mut z = 0u32;
+ let z = &mut z;
+
+ core::mem::swap(y, z);
+}
diff --git a/src/tools/clippy/tests/ui/swap_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/swap_ptr_to_ref.stderr
new file mode 100644
index 000000000..401ce0708
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap_ptr_to_ref.stderr
@@ -0,0 +1,28 @@
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref.rs:13:9
+ |
+LL | core::mem::swap(&mut *y, &mut *z);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, z)`
+ |
+ = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings`
+
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref.rs:14:9
+ |
+LL | core::mem::swap(&mut *y, &mut x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, &mut x)`
+
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref.rs:15:9
+ |
+LL | core::mem::swap(&mut x, &mut *y);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(&mut x, y)`
+
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref.rs:16:9
+ |
+LL | core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x))`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.rs b/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.rs
new file mode 100644
index 000000000..66ea7c652
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.rs
@@ -0,0 +1,18 @@
+#![warn(clippy::swap_ptr_to_ref)]
+
+macro_rules! addr_of_mut_to_ref {
+ ($e:expr) => {
+ &mut *core::ptr::addr_of_mut!($e)
+ };
+}
+
+fn main() {
+ let mut x = 0u32;
+ let y: *mut _ = &mut x;
+
+ unsafe {
+ core::mem::swap(addr_of_mut_to_ref!(x), &mut *y);
+ core::mem::swap(&mut *y, addr_of_mut_to_ref!(x));
+ core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.stderr b/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.stderr
new file mode 100644
index 000000000..c261205d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.stderr
@@ -0,0 +1,22 @@
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref_unfixable.rs:14:9
+ |
+LL | core::mem::swap(addr_of_mut_to_ref!(x), &mut *y);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings`
+
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref_unfixable.rs:15:9
+ |
+LL | core::mem::swap(&mut *y, addr_of_mut_to_ref!(x));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: call to `core::mem::swap` with a parameter derived from a raw pointer
+ --> $DIR/swap_ptr_to_ref_unfixable.rs:16:9
+ |
+LL | core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
new file mode 100644
index 000000000..4bc4bc86c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::tabs_in_doc_comments)]
+#[allow(dead_code)]
+
+///
+/// Struct to hold two strings:
+/// - first one
+/// - second one
+pub struct DoubleString {
+ ///
+ /// - First String:
+ /// - needs to be inside here
+ first_string: String,
+ ///
+ /// - Second String:
+ /// - needs to be inside here
+ second_string: String,
+}
+
+/// This is main
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
new file mode 100644
index 000000000..9db3416e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::tabs_in_doc_comments)]
+#[allow(dead_code)]
+
+///
+/// Struct to hold two strings:
+/// - first one
+/// - second one
+pub struct DoubleString {
+ ///
+ /// - First String:
+ /// - needs to be inside here
+ first_string: String,
+ ///
+ /// - Second String:
+ /// - needs to be inside here
+ second_string: String,
+}
+
+/// This is main
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
new file mode 100644
index 000000000..355f2e805
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
@@ -0,0 +1,52 @@
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:12:9
+ |
+LL | /// - First String:
+ | ^^^^ help: consider using four spaces per tab
+ |
+ = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:13:9
+ |
+LL | /// - needs to be inside here
+ | ^^^^^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:16:9
+ |
+LL | /// - Second String:
+ | ^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:17:9
+ |
+LL | /// - needs to be inside here
+ | ^^^^^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:8:5
+ |
+LL | /// - first one
+ | ^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:8:13
+ |
+LL | /// - first one
+ | ^^^^^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:9:5
+ |
+LL | /// - second one
+ | ^^^^ help: consider using four spaces per tab
+
+error: using tabs in doc comments is not recommended
+ --> $DIR/tabs_in_doc_comments.rs:9:14
+ |
+LL | /// - second one
+ | ^^^^ help: consider using four spaces per tab
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/temporary_assignment.rs b/src/tools/clippy/tests/ui/temporary_assignment.rs
new file mode 100644
index 000000000..b4a931043
--- /dev/null
+++ b/src/tools/clippy/tests/ui/temporary_assignment.rs
@@ -0,0 +1,71 @@
+#![warn(clippy::temporary_assignment)]
+#![allow(const_item_mutation)]
+
+use std::ops::{Deref, DerefMut};
+
+struct TupleStruct(i32);
+
+struct Struct {
+ field: i32,
+}
+
+struct MultiStruct {
+ structure: Struct,
+}
+
+struct Wrapper<'a> {
+ inner: &'a mut Struct,
+}
+
+impl<'a> Deref for Wrapper<'a> {
+ type Target = Struct;
+ fn deref(&self) -> &Struct {
+ self.inner
+ }
+}
+
+impl<'a> DerefMut for Wrapper<'a> {
+ fn deref_mut(&mut self) -> &mut Struct {
+ self.inner
+ }
+}
+
+struct ArrayStruct {
+ array: [i32; 1],
+}
+
+const A: TupleStruct = TupleStruct(1);
+const B: Struct = Struct { field: 1 };
+const C: MultiStruct = MultiStruct {
+ structure: Struct { field: 1 },
+};
+const D: ArrayStruct = ArrayStruct { array: [1] };
+
+fn main() {
+ let mut s = Struct { field: 0 };
+ let mut t = (0, 0);
+
+ Struct { field: 0 }.field = 1;
+ MultiStruct {
+ structure: Struct { field: 0 },
+ }
+ .structure
+ .field = 1;
+ ArrayStruct { array: [0] }.array[0] = 1;
+ (0, 0).0 = 1;
+
+ // no error
+ s.field = 1;
+ t.0 = 1;
+ Wrapper { inner: &mut s }.field = 1;
+ let mut a_mut = TupleStruct(1);
+ a_mut.0 = 2;
+ let mut b_mut = Struct { field: 1 };
+ b_mut.field = 2;
+ let mut c_mut = MultiStruct {
+ structure: Struct { field: 1 },
+ };
+ c_mut.structure.field = 2;
+ let mut d_mut = ArrayStruct { array: [1] };
+ d_mut.array[0] = 2;
+}
diff --git a/src/tools/clippy/tests/ui/temporary_assignment.stderr b/src/tools/clippy/tests/ui/temporary_assignment.stderr
new file mode 100644
index 000000000..4cc32c79f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/temporary_assignment.stderr
@@ -0,0 +1,32 @@
+error: assignment to temporary
+ --> $DIR/temporary_assignment.rs:48:5
+ |
+LL | Struct { field: 0 }.field = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::temporary-assignment` implied by `-D warnings`
+
+error: assignment to temporary
+ --> $DIR/temporary_assignment.rs:49:5
+ |
+LL | / MultiStruct {
+LL | | structure: Struct { field: 0 },
+LL | | }
+LL | | .structure
+LL | | .field = 1;
+ | |______________^
+
+error: assignment to temporary
+ --> $DIR/temporary_assignment.rs:54:5
+ |
+LL | ArrayStruct { array: [0] }.array[0] = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: assignment to temporary
+ --> $DIR/temporary_assignment.rs:55:5
+ |
+LL | (0, 0).0 = 1;
+ | ^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed
new file mode 100644
index 000000000..3c5e96427
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed
@@ -0,0 +1,11 @@
+//run-rustfix
+
+#![warn(clippy::to_digit_is_some)]
+
+fn main() {
+ let c = 'x';
+ let d = &c;
+
+ let _ = d.is_digit(8);
+ let _ = char::is_digit(c, 8);
+}
diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs
new file mode 100644
index 000000000..4f247c06c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs
@@ -0,0 +1,11 @@
+//run-rustfix
+
+#![warn(clippy::to_digit_is_some)]
+
+fn main() {
+ let c = 'x';
+ let d = &c;
+
+ let _ = d.to_digit(8).is_some();
+ let _ = char::to_digit(c, 8).is_some();
+}
diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.stderr b/src/tools/clippy/tests/ui/to_digit_is_some.stderr
new file mode 100644
index 000000000..10a1b393a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr
@@ -0,0 +1,16 @@
+error: use of `.to_digit(..).is_some()`
+ --> $DIR/to_digit_is_some.rs:9:13
+ |
+LL | let _ = d.to_digit(8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)`
+ |
+ = note: `-D clippy::to-digit-is-some` implied by `-D warnings`
+
+error: use of `.to_digit(..).is_some()`
+ --> $DIR/to_digit_is_some.rs:10:13
+ |
+LL | let _ = char::to_digit(c, 8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
new file mode 100644
index 000000000..b129d95c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
@@ -0,0 +1,50 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::toplevel_ref_arg)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! gen_binding {
+ () => {
+ let _y = &42;
+ };
+}
+
+fn main() {
+ // Closures should not warn
+ let y = |ref x| println!("{:?}", x);
+ y(1u8);
+
+ let _x = &1;
+
+ let _y: &(&_, u8) = &(&1, 2);
+
+ let _z = &(1 + 2);
+
+ let _z = &mut (1 + 2);
+
+ let (ref x, _) = (1, 2); // ok, not top level
+ println!("The answer is {}.", x);
+
+ let _x = &vec![1, 2, 3];
+
+ // Make sure that allowing the lint works
+ #[allow(clippy::toplevel_ref_arg)]
+ let ref mut _x = 1_234_543;
+
+ // ok
+ for ref _x in 0..10 {}
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_binding!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_binding!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
new file mode 100644
index 000000000..73eb4ff73
--- /dev/null
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
@@ -0,0 +1,50 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::toplevel_ref_arg)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! gen_binding {
+ () => {
+ let ref _y = 42;
+ };
+}
+
+fn main() {
+ // Closures should not warn
+ let y = |ref x| println!("{:?}", x);
+ y(1u8);
+
+ let ref _x = 1;
+
+ let ref _y: (&_, u8) = (&1, 2);
+
+ let ref _z = 1 + 2;
+
+ let ref mut _z = 1 + 2;
+
+ let (ref x, _) = (1, 2); // ok, not top level
+ println!("The answer is {}.", x);
+
+ let ref _x = vec![1, 2, 3];
+
+ // Make sure that allowing the lint works
+ #[allow(clippy::toplevel_ref_arg)]
+ let ref mut _x = 1_234_543;
+
+ // ok
+ for ref _x in 0..10 {}
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_binding!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_binding!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
new file mode 100644
index 000000000..9c853020a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
@@ -0,0 +1,45 @@
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:20:9
+ |
+LL | let ref _x = 1;
+ | ----^^^^^^----- help: try: `let _x = &1;`
+ |
+ = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
+
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:22:9
+ |
+LL | let ref _y: (&_, u8) = (&1, 2);
+ | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);`
+
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:24:9
+ |
+LL | let ref _z = 1 + 2;
+ | ----^^^^^^--------- help: try: `let _z = &(1 + 2);`
+
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:26:9
+ |
+LL | let ref mut _z = 1 + 2;
+ | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);`
+
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:31:9
+ |
+LL | let ref _x = vec![1, 2, 3];
+ | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];`
+
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:11:13
+ |
+LL | let ref _y = 42;
+ | ----^^^^^^------ help: try: `let _y = &42;`
+...
+LL | gen_binding!();
+ | -------------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen_binding` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
new file mode 100644
index 000000000..1a493fbce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
@@ -0,0 +1,33 @@
+// aux-build:macro_rules.rs
+
+#![warn(clippy::toplevel_ref_arg)]
+#![allow(unused)]
+
+#[macro_use]
+extern crate macro_rules;
+
+fn the_answer(ref mut x: u8) {
+ *x = 42;
+}
+
+macro_rules! gen_function {
+ () => {
+ fn fun_example(ref _x: usize) {}
+ };
+}
+
+fn main() {
+ let mut x = 0;
+ the_answer(x);
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_function!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_function!();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
new file mode 100644
index 000000000..e97011c7f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
@@ -0,0 +1,21 @@
+error: `ref` directly on a function argument is ignored. Consider using a reference type instead
+ --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15
+ |
+LL | fn the_answer(ref mut x: u8) {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
+
+error: `ref` directly on a function argument is ignored. Consider using a reference type instead
+ --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24
+ |
+LL | fn fun_example(ref _x: usize) {}
+ | ^^^^^^
+...
+LL | gen_function!();
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in the macro `gen_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs
new file mode 100644
index 000000000..c39b0bcaf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs
@@ -0,0 +1,185 @@
+#![warn(clippy::trailing_empty_array)]
+
+// Do lint:
+
+struct RarelyUseful {
+ field: i32,
+ last: [usize; 0],
+}
+
+struct OnlyField {
+ first_and_last: [usize; 0],
+}
+
+struct GenericArrayType<T> {
+ field: i32,
+ last: [T; 0],
+}
+
+#[must_use]
+struct OnlyAnotherAttribute {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[derive(Debug)]
+struct OnlyADeriveAttribute {
+ field: i32,
+ last: [usize; 0],
+}
+
+const ZERO: usize = 0;
+struct ZeroSizedWithConst {
+ field: i32,
+ last: [usize; ZERO],
+}
+
+#[allow(clippy::eq_op)]
+const fn compute_zero() -> usize {
+ (4 + 6) - (2 * 5)
+}
+struct ZeroSizedWithConstFunction {
+ field: i32,
+ last: [usize; compute_zero()],
+}
+
+const fn compute_zero_from_arg(x: usize) -> usize {
+ x - 1
+}
+struct ZeroSizedWithConstFunction2 {
+ field: i32,
+ last: [usize; compute_zero_from_arg(1)],
+}
+
+struct ZeroSizedArrayWrapper([usize; 0]);
+
+struct TupleStruct(i32, [usize; 0]);
+
+struct LotsOfFields {
+ f1: u32,
+ f2: u32,
+ f3: u32,
+ f4: u32,
+ f5: u32,
+ f6: u32,
+ f7: u32,
+ f8: u32,
+ f9: u32,
+ f10: u32,
+ f11: u32,
+ f12: u32,
+ f13: u32,
+ f14: u32,
+ f15: u32,
+ f16: u32,
+ last: [usize; 0],
+}
+
+// Don't lint
+
+#[repr(C)]
+struct GoodReason {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[repr(C)]
+struct OnlyFieldWithReprC {
+ first_and_last: [usize; 0],
+}
+
+struct NonZeroSizedArray {
+ field: i32,
+ last: [usize; 1],
+}
+
+struct NotLastField {
+ f1: u32,
+ zero_sized: [usize; 0],
+ last: i32,
+}
+
+const ONE: usize = 1;
+struct NonZeroSizedWithConst {
+ field: i32,
+ last: [usize; ONE],
+}
+
+#[derive(Debug)]
+#[repr(C)]
+struct AlsoADeriveAttribute {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[must_use]
+#[repr(C)]
+struct AlsoAnotherAttribute {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[repr(packed)]
+struct ReprPacked {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[repr(C, packed)]
+struct ReprCPacked {
+ field: i32,
+ last: [usize; 0],
+}
+
+#[repr(align(64))]
+struct ReprAlign {
+ field: i32,
+ last: [usize; 0],
+}
+#[repr(C, align(64))]
+struct ReprCAlign {
+ field: i32,
+ last: [usize; 0],
+}
+
+// NOTE: because of https://doc.rust-lang.org/stable/reference/type-layout.html#primitive-representation-of-enums-with-fields and I'm not sure when in the compilation pipeline that would happen
+#[repr(C)]
+enum DontLintAnonymousStructsFromDesuraging {
+ A(u32),
+ B(f32, [u64; 0]),
+ C { x: u32, y: [u64; 0] },
+}
+
+#[repr(C)]
+struct TupleStructReprC(i32, [usize; 0]);
+
+type NamedTuple = (i32, [usize; 0]);
+
+#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
+struct ConstParamZeroDefault<const N: usize = 0> {
+ field: i32,
+ last: [usize; N],
+}
+
+struct ConstParamNoDefault<const N: usize> {
+ field: i32,
+ last: [usize; N],
+}
+
+#[rustfmt::skip]
+struct ConstParamNonZeroDefault<const N: usize = 1> {
+ field: i32,
+ last: [usize; N],
+}
+
+struct TwoGenericParams<T, const N: usize> {
+ field: i32,
+ last: [T; N],
+}
+
+type A = ConstParamZeroDefault;
+type B = ConstParamZeroDefault<0>;
+type C = ConstParamNoDefault<0>;
+type D = ConstParamNonZeroDefault<0>;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.stderr b/src/tools/clippy/tests/ui/trailing_empty_array.stderr
new file mode 100644
index 000000000..9e2bd31d9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.stderr
@@ -0,0 +1,120 @@
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:5:1
+ |
+LL | / struct RarelyUseful {
+LL | | field: i32,
+LL | | last: [usize; 0],
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::trailing-empty-array` implied by `-D warnings`
+ = help: consider annotating `RarelyUseful` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:10:1
+ |
+LL | / struct OnlyField {
+LL | | first_and_last: [usize; 0],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:14:1
+ |
+LL | / struct GenericArrayType<T> {
+LL | | field: i32,
+LL | | last: [T; 0],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:20:1
+ |
+LL | / struct OnlyAnotherAttribute {
+LL | | field: i32,
+LL | | last: [usize; 0],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:26:1
+ |
+LL | / struct OnlyADeriveAttribute {
+LL | | field: i32,
+LL | | last: [usize; 0],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:32:1
+ |
+LL | / struct ZeroSizedWithConst {
+LL | | field: i32,
+LL | | last: [usize; ZERO],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:41:1
+ |
+LL | / struct ZeroSizedWithConstFunction {
+LL | | field: i32,
+LL | | last: [usize; compute_zero()],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:49:1
+ |
+LL | / struct ZeroSizedWithConstFunction2 {
+LL | | field: i32,
+LL | | last: [usize; compute_zero_from_arg(1)],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:54:1
+ |
+LL | struct ZeroSizedArrayWrapper([usize; 0]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:56:1
+ |
+LL | struct TupleStruct(i32, [usize; 0]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute
+
+error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
+ --> $DIR/trailing_empty_array.rs:58:1
+ |
+LL | / struct LotsOfFields {
+LL | | f1: u32,
+LL | | f2: u32,
+LL | | f3: u32,
+... |
+LL | | last: [usize; 0],
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `LotsOfFields` with `#[repr(C)]` or another `repr` attribute
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trailing_zeros.rs b/src/tools/clippy/tests/ui/trailing_zeros.rs
new file mode 100644
index 000000000..fbdc977b7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trailing_zeros.rs
@@ -0,0 +1,10 @@
+#![allow(unused_parens)]
+#![warn(clippy::verbose_bit_mask)]
+
+fn main() {
+ let x: i32 = 42;
+ let _ = (x & 0b1111 == 0); // suggest trailing_zeros
+ let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
+ let _ = x & 0b1_1010 == 0; // do not lint
+ let _ = x & 1 == 0; // do not lint
+}
diff --git a/src/tools/clippy/tests/ui/trailing_zeros.stderr b/src/tools/clippy/tests/ui/trailing_zeros.stderr
new file mode 100644
index 000000000..798551118
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trailing_zeros.stderr
@@ -0,0 +1,16 @@
+error: bit mask could be simplified with a call to `trailing_zeros`
+ --> $DIR/trailing_zeros.rs:6:13
+ |
+LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros
+ | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4`
+ |
+ = note: `-D clippy::verbose-bit-mask` implied by `-D warnings`
+
+error: bit mask could be simplified with a call to `trailing_zeros`
+ --> $DIR/trailing_zeros.rs:7:13
+ |
+LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
+ | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
new file mode 100644
index 000000000..a5751c58a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
@@ -0,0 +1,212 @@
+#![deny(clippy::trait_duplication_in_bounds)]
+#![allow(unused)]
+
+use std::collections::BTreeMap;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+where
+ T: Clone,
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn good_bar<T: Clone + Default>(arg: T) {
+ unimplemented!();
+}
+
+fn good_foo<T>(arg: T)
+where
+ T: Clone + Default,
+{
+ unimplemented!();
+}
+
+fn good_foobar<T: Default>(arg: T)
+where
+ T: Clone,
+{
+ unimplemented!();
+}
+
+trait T: Default {
+ fn f()
+ where
+ Self: Default;
+}
+
+trait U: Default {
+ fn f()
+ where
+ Self: Clone;
+}
+
+trait ZZ: Default {
+ fn g();
+ fn h();
+ fn f()
+ where
+ Self: Default + Clone;
+}
+
+trait BadTrait: Default + Clone {
+ fn f()
+ where
+ Self: Default + Clone;
+ fn g()
+ where
+ Self: Default;
+ fn h()
+ where
+ Self: Copy;
+}
+
+#[derive(Default, Clone)]
+struct Life;
+
+impl T for Life {
+ // this should not warn
+ fn f() {}
+}
+
+impl U for Life {
+ // this should not warn
+ fn f() {}
+}
+
+// should not warn
+trait Iter: Iterator {
+ fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
+ where
+ Self: Iterator<Item = (K, V)> + Sized,
+ K: Ord + Eq,
+ {
+ unimplemented!();
+ }
+}
+
+struct Foo;
+
+trait FooIter: Iterator<Item = Foo> {
+ fn bar()
+ where
+ Self: Iterator<Item = Foo>,
+ {
+ }
+}
+
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
+mod repeated_where_clauses_or_trait_bounds {
+ fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ unimplemented!();
+ }
+
+ fn bad_bar<T, U>(arg0: T, arg1: U)
+ where
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy,
+ {
+ unimplemented!();
+ }
+
+ fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
+ unimplemented!();
+ }
+
+ fn good_foo<T, U>(arg0: T, arg1: U)
+ where
+ T: Clone + Copy,
+ U: Clone + Copy,
+ {
+ unimplemented!();
+ }
+
+ trait GoodSelfTraitBound: Clone + Copy {
+ fn f();
+ }
+
+ trait GoodSelfWhereClause {
+ fn f()
+ where
+ Self: Clone + Copy;
+ }
+
+ trait BadSelfTraitBound: Clone + Clone + Clone {
+ fn f();
+ }
+
+ trait BadSelfWhereClause {
+ fn f()
+ where
+ Self: Clone + Clone + Clone;
+ }
+
+ trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+ fn f();
+ }
+
+ trait GoodWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Copy,
+ U: Clone + Copy;
+ }
+
+ trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ fn f();
+ }
+
+ trait BadWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy;
+ }
+
+ struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+ t: T,
+ u: U,
+ }
+
+ impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
+ // this should not warn
+ fn f() {}
+ }
+
+ struct GoodStructWhereClause;
+
+ impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+ where
+ T: Clone + Copy,
+ U: Clone + Copy,
+ {
+ // this should not warn
+ fn f() {}
+ }
+
+ fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
+
+ trait GenericTrait<T> {}
+
+ // This should not warn but currently does see #8757
+ fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ unimplemented!();
+ }
+
+ fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ unimplemented!();
+ }
+
+ mod foo {
+ pub trait Clone {}
+ }
+
+ fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ unimplemented!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
new file mode 100644
index 000000000..7ef04e527
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
@@ -0,0 +1,167 @@
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:7:15
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/trait_duplication_in_bounds.rs:1:9
+ |
+LL | #![deny(clippy::trait_duplication_in_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:7:23
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:36:15
+ |
+LL | Self: Default;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:50:15
+ |
+LL | Self: Default + Clone;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:56:15
+ |
+LL | Self: Default + Clone;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:56:25
+ |
+LL | Self: Default + Clone;
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:59:15
+ |
+LL | Self: Default;
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+ --> $DIR/trait_duplication_in_bounds.rs:94:15
+ |
+LL | Self: Iterator<Item = Foo>,
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:103:19
+ |
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:103:19
+ |
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:109:12
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:109:12
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:137:30
+ |
+LL | trait BadSelfTraitBound: Clone + Clone + Clone {
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:144:19
+ |
+LL | Self: Clone + Clone + Clone;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:158:28
+ |
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:158:28
+ |
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:165:16
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:195:24
+ |
+LL | fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:199:23
+ |
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:199:23
+ |
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:207:26
+ |
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:207:26
+ |
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs
new file mode 100644
index 000000000..001c91023
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute.rs
@@ -0,0 +1,162 @@
+#![allow(dead_code, clippy::borrow_as_ptr)]
+
+extern crate core;
+
+use std::mem::transmute as my_transmute;
+use std::vec::Vec as MyVec;
+
+fn my_int() -> Usize {
+ Usize(42)
+}
+
+fn my_vec() -> MyVec<i32> {
+ vec![]
+}
+
+#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)]
+#[warn(clippy::useless_transmute)]
+unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
+ // FIXME: should lint
+ // let _: &'a T = core::intrinsics::transmute(t);
+
+ let _: &'a U = core::intrinsics::transmute(t);
+
+ let _: *const T = core::intrinsics::transmute(t);
+
+ let _: *mut T = core::intrinsics::transmute(t);
+
+ let _: *const U = core::intrinsics::transmute(t);
+}
+
+#[warn(clippy::useless_transmute)]
+fn useless() {
+ unsafe {
+ let _: Vec<i32> = core::intrinsics::transmute(my_vec());
+
+ let _: Vec<i32> = core::mem::transmute(my_vec());
+
+ let _: Vec<i32> = std::intrinsics::transmute(my_vec());
+
+ let _: Vec<i32> = std::mem::transmute(my_vec());
+
+ let _: Vec<i32> = my_transmute(my_vec());
+
+ let _: *const usize = std::mem::transmute(5_isize);
+
+ let _ = 5_isize as *const usize;
+
+ let _: *const usize = std::mem::transmute(1 + 1usize);
+
+ let _ = (1 + 1_usize) as *const usize;
+ }
+
+ unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 {
+ std::mem::transmute(x)
+ }
+
+ unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator<Item = u32> + 'a)) -> *const (dyn Iterator<Item = u32> + 'b) {
+ std::mem::transmute(x)
+ }
+
+ unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) {
+ std::mem::transmute(x)
+ }
+
+ unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> {
+ std::mem::transmute(x)
+ }
+}
+
+struct Usize(usize);
+
+#[warn(clippy::crosspointer_transmute)]
+fn crosspointer() {
+ let mut int: Usize = Usize(0);
+ let int_const_ptr: *const Usize = &int as *const Usize;
+ let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
+
+ unsafe {
+ let _: Usize = core::intrinsics::transmute(int_const_ptr);
+
+ let _: Usize = core::intrinsics::transmute(int_mut_ptr);
+
+ let _: *const Usize = core::intrinsics::transmute(my_int());
+
+ let _: *mut Usize = core::intrinsics::transmute(my_int());
+ }
+}
+
+#[warn(clippy::transmute_int_to_char)]
+fn int_to_char() {
+ let _: char = unsafe { std::mem::transmute(0_u32) };
+ let _: char = unsafe { std::mem::transmute(0_i32) };
+
+ // These shouldn't warn
+ const _: char = unsafe { std::mem::transmute(0_u32) };
+ const _: char = unsafe { std::mem::transmute(0_i32) };
+}
+
+#[warn(clippy::transmute_int_to_bool)]
+fn int_to_bool() {
+ let _: bool = unsafe { std::mem::transmute(0_u8) };
+}
+
+#[warn(clippy::transmute_int_to_float)]
+mod int_to_float {
+ fn test() {
+ let _: f32 = unsafe { std::mem::transmute(0_u32) };
+ let _: f32 = unsafe { std::mem::transmute(0_i32) };
+ let _: f64 = unsafe { std::mem::transmute(0_u64) };
+ let _: f64 = unsafe { std::mem::transmute(0_i64) };
+ }
+
+ mod issue_5747 {
+ const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) };
+ const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) };
+
+ const fn from_bits_32(v: i32) -> f32 {
+ unsafe { std::mem::transmute(v) }
+ }
+
+ const fn from_bits_64(v: u64) -> f64 {
+ unsafe { std::mem::transmute(v) }
+ }
+ }
+}
+
+mod num_to_bytes {
+ fn test() {
+ unsafe {
+ let _: [u8; 1] = std::mem::transmute(0u8);
+ let _: [u8; 4] = std::mem::transmute(0u32);
+ let _: [u8; 16] = std::mem::transmute(0u128);
+ let _: [u8; 1] = std::mem::transmute(0i8);
+ let _: [u8; 4] = std::mem::transmute(0i32);
+ let _: [u8; 16] = std::mem::transmute(0i128);
+ let _: [u8; 4] = std::mem::transmute(0.0f32);
+ let _: [u8; 8] = std::mem::transmute(0.0f64);
+ }
+ }
+ const fn test_const() {
+ unsafe {
+ let _: [u8; 1] = std::mem::transmute(0u8);
+ let _: [u8; 4] = std::mem::transmute(0u32);
+ let _: [u8; 16] = std::mem::transmute(0u128);
+ let _: [u8; 1] = std::mem::transmute(0i8);
+ let _: [u8; 4] = std::mem::transmute(0i32);
+ let _: [u8; 16] = std::mem::transmute(0i128);
+ let _: [u8; 4] = std::mem::transmute(0.0f32);
+ let _: [u8; 8] = std::mem::transmute(0.0f64);
+ }
+ }
+}
+
+fn bytes_to_str(mb: &mut [u8]) {
+ const B: &[u8] = b"";
+
+ let _: &str = unsafe { std::mem::transmute(B) };
+ let _: &mut str = unsafe { std::mem::transmute(mb) };
+ const _: &str = unsafe { std::mem::transmute(B) };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr
new file mode 100644
index 000000000..008b4a981
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute.stderr
@@ -0,0 +1,244 @@
+error: transmute from a reference to a pointer
+ --> $DIR/transmute.rs:24:23
+ |
+LL | let _: *const T = core::intrinsics::transmute(t);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
+ |
+ = note: `-D clippy::useless-transmute` implied by `-D warnings`
+
+error: transmute from a reference to a pointer
+ --> $DIR/transmute.rs:26:21
+ |
+LL | let _: *mut T = core::intrinsics::transmute(t);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
+
+error: transmute from a reference to a pointer
+ --> $DIR/transmute.rs:28:23
+ |
+LL | let _: *const U = core::intrinsics::transmute(t);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
+
+error: transmute from a type (`std::vec::Vec<i32>`) to itself
+ --> $DIR/transmute.rs:34:27
+ |
+LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`std::vec::Vec<i32>`) to itself
+ --> $DIR/transmute.rs:36:27
+ |
+LL | let _: Vec<i32> = core::mem::transmute(my_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`std::vec::Vec<i32>`) to itself
+ --> $DIR/transmute.rs:38:27
+ |
+LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`std::vec::Vec<i32>`) to itself
+ --> $DIR/transmute.rs:40:27
+ |
+LL | let _: Vec<i32> = std::mem::transmute(my_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`std::vec::Vec<i32>`) to itself
+ --> $DIR/transmute.rs:42:27
+ |
+LL | let _: Vec<i32> = my_transmute(my_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from an integer to a pointer
+ --> $DIR/transmute.rs:44:31
+ |
+LL | let _: *const usize = std::mem::transmute(5_isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
+
+error: transmute from an integer to a pointer
+ --> $DIR/transmute.rs:48:31
+ |
+LL | let _: *const usize = std::mem::transmute(1 + 1usize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
+
+error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
+ --> $DIR/transmute.rs:79:24
+ |
+LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
+
+error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
+ --> $DIR/transmute.rs:81:24
+ |
+LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
+ --> $DIR/transmute.rs:83:31
+ |
+LL | let _: *const Usize = core::intrinsics::transmute(my_int());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
+ --> $DIR/transmute.rs:85:29
+ |
+LL | let _: *mut Usize = core::intrinsics::transmute(my_int());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a `u32` to a `char`
+ --> $DIR/transmute.rs:91:28
+ |
+LL | let _: char = unsafe { std::mem::transmute(0_u32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
+ |
+ = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
+
+error: transmute from a `i32` to a `char`
+ --> $DIR/transmute.rs:92:28
+ |
+LL | let _: char = unsafe { std::mem::transmute(0_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
+
+error: transmute from a `u8` to a `bool`
+ --> $DIR/transmute.rs:101:28
+ |
+LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
+ |
+ = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
+
+error: transmute from a `u32` to a `f32`
+ --> $DIR/transmute.rs:107:31
+ |
+LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
+ |
+ = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
+
+error: transmute from a `i32` to a `f32`
+ --> $DIR/transmute.rs:108:31
+ |
+LL | let _: f32 = unsafe { std::mem::transmute(0_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
+
+error: transmute from a `u64` to a `f64`
+ --> $DIR/transmute.rs:109:31
+ |
+LL | let _: f64 = unsafe { std::mem::transmute(0_u64) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
+
+error: transmute from a `i64` to a `f64`
+ --> $DIR/transmute.rs:110:31
+ |
+LL | let _: f64 = unsafe { std::mem::transmute(0_i64) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
+
+error: transmute from a `u8` to a `[u8; 1]`
+ --> $DIR/transmute.rs:130:30
+ |
+LL | let _: [u8; 1] = std::mem::transmute(0u8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
+ |
+ = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings`
+
+error: transmute from a `u32` to a `[u8; 4]`
+ --> $DIR/transmute.rs:131:30
+ |
+LL | let _: [u8; 4] = std::mem::transmute(0u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
+
+error: transmute from a `u128` to a `[u8; 16]`
+ --> $DIR/transmute.rs:132:31
+ |
+LL | let _: [u8; 16] = std::mem::transmute(0u128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
+
+error: transmute from a `i8` to a `[u8; 1]`
+ --> $DIR/transmute.rs:133:30
+ |
+LL | let _: [u8; 1] = std::mem::transmute(0i8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
+
+error: transmute from a `i32` to a `[u8; 4]`
+ --> $DIR/transmute.rs:134:30
+ |
+LL | let _: [u8; 4] = std::mem::transmute(0i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
+
+error: transmute from a `i128` to a `[u8; 16]`
+ --> $DIR/transmute.rs:135:31
+ |
+LL | let _: [u8; 16] = std::mem::transmute(0i128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
+
+error: transmute from a `f32` to a `[u8; 4]`
+ --> $DIR/transmute.rs:136:30
+ |
+LL | let _: [u8; 4] = std::mem::transmute(0.0f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()`
+
+error: transmute from a `f64` to a `[u8; 8]`
+ --> $DIR/transmute.rs:137:30
+ |
+LL | let _: [u8; 8] = std::mem::transmute(0.0f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()`
+
+error: transmute from a `u8` to a `[u8; 1]`
+ --> $DIR/transmute.rs:142:30
+ |
+LL | let _: [u8; 1] = std::mem::transmute(0u8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
+
+error: transmute from a `u32` to a `[u8; 4]`
+ --> $DIR/transmute.rs:143:30
+ |
+LL | let _: [u8; 4] = std::mem::transmute(0u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
+
+error: transmute from a `u128` to a `[u8; 16]`
+ --> $DIR/transmute.rs:144:31
+ |
+LL | let _: [u8; 16] = std::mem::transmute(0u128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
+
+error: transmute from a `i8` to a `[u8; 1]`
+ --> $DIR/transmute.rs:145:30
+ |
+LL | let _: [u8; 1] = std::mem::transmute(0i8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
+
+error: transmute from a `i32` to a `[u8; 4]`
+ --> $DIR/transmute.rs:146:30
+ |
+LL | let _: [u8; 4] = std::mem::transmute(0i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
+
+error: transmute from a `i128` to a `[u8; 16]`
+ --> $DIR/transmute.rs:147:31
+ |
+LL | let _: [u8; 16] = std::mem::transmute(0i128);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
+
+error: transmute from a `&[u8]` to a `&str`
+ --> $DIR/transmute.rs:157:28
+ |
+LL | let _: &str = unsafe { std::mem::transmute(B) };
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()`
+ |
+ = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
+
+error: transmute from a `&mut [u8]` to a `&mut str`
+ --> $DIR/transmute.rs:158:32
+ |
+LL | let _: &mut str = unsafe { std::mem::transmute(mb) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
+
+error: transmute from a `&[u8]` to a `&str`
+ --> $DIR/transmute.rs:159:30
+ |
+LL | const _: &str = unsafe { std::mem::transmute(B) };
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
+
+error: aborting due to 38 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_32bit.rs b/src/tools/clippy/tests/ui/transmute_32bit.rs
new file mode 100644
index 000000000..ffe22b12f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_32bit.rs
@@ -0,0 +1,14 @@
+// ignore-64bit
+
+#[warn(clippy::wrong_transmute)]
+fn main() {
+ unsafe {
+ let _: *const usize = std::mem::transmute(6.0f32);
+
+ let _: *mut usize = std::mem::transmute(6.0f32);
+
+ let _: *const usize = std::mem::transmute('x');
+
+ let _: *mut usize = std::mem::transmute('x');
+ }
+}
diff --git a/src/tools/clippy/tests/ui/transmute_32bit.stderr b/src/tools/clippy/tests/ui/transmute_32bit.stderr
new file mode 100644
index 000000000..040519564
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_32bit.stderr
@@ -0,0 +1,28 @@
+error: transmute from a `f32` to a pointer
+ --> $DIR/transmute_32bit.rs:6:31
+ |
+LL | let _: *const usize = std::mem::transmute(6.0f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-transmute` implied by `-D warnings`
+
+error: transmute from a `f32` to a pointer
+ --> $DIR/transmute_32bit.rs:8:29
+ |
+LL | let _: *mut usize = std::mem::transmute(6.0f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a `char` to a pointer
+ --> $DIR/transmute_32bit.rs:10:31
+ |
+LL | let _: *const usize = std::mem::transmute('x');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from a `char` to a pointer
+ --> $DIR/transmute_32bit.rs:12:29
+ |
+LL | let _: *mut usize = std::mem::transmute('x');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_64bit.rs b/src/tools/clippy/tests/ui/transmute_64bit.rs
new file mode 100644
index 000000000..00dc0b2c3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_64bit.rs
@@ -0,0 +1,10 @@
+// ignore-32bit
+
+#[warn(clippy::wrong_transmute)]
+fn main() {
+ unsafe {
+ let _: *const usize = std::mem::transmute(6.0f64);
+
+ let _: *mut usize = std::mem::transmute(6.0f64);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/transmute_64bit.stderr b/src/tools/clippy/tests/ui/transmute_64bit.stderr
new file mode 100644
index 000000000..d1854c009
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_64bit.stderr
@@ -0,0 +1,16 @@
+error: transmute from a `f64` to a pointer
+ --> $DIR/transmute_64bit.rs:6:31
+ |
+LL | let _: *const usize = std::mem::transmute(6.0f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-transmute` implied by `-D warnings`
+
+error: transmute from a `f64` to a pointer
+ --> $DIR/transmute_64bit.rs:8:29
+ |
+LL | let _: *mut usize = std::mem::transmute(6.0f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs
new file mode 100644
index 000000000..5a431bee0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_collection.rs
@@ -0,0 +1,50 @@
+#![warn(clippy::unsound_collection_transmute)]
+
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
+use std::mem::{transmute, MaybeUninit};
+
+fn main() {
+ unsafe {
+ // wrong size
+ let _ = transmute::<_, Vec<u32>>(vec![0u8]);
+ // wrong layout
+ let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
+
+ // wrong size
+ let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
+ // wrong layout
+ let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
+
+ // wrong size
+ let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
+ // wrong layout
+ let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
+
+ // wrong size
+ let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
+ // wrong layout
+ let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
+
+ // wrong size
+ let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
+ // wrong layout
+ let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
+
+ // wrong size
+ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
+ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
+ // wrong layout
+ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
+ let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
+
+ // wrong size
+ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
+ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
+ // wrong layout
+ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
+ let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+
+ let _ = transmute::<_, Vec<u8>>(Vec::<MaybeUninit<u8>>::new());
+ let _ = transmute::<_, Vec<*mut u32>>(Vec::<Box<u32>>::new());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/transmute_collection.stderr b/src/tools/clippy/tests/ui/transmute_collection.stderr
new file mode 100644
index 000000000..ebc05c402
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_collection.stderr
@@ -0,0 +1,112 @@
+error: transmute from `std::vec::Vec<u8>` to `std::vec::Vec<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:9:17
+ |
+LL | let _ = transmute::<_, Vec<u32>>(vec![0u8]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings`
+
+error: transmute from `std::vec::Vec<u32>` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:11:17
+ |
+LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::VecDeque<u8>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:14:17
+ |
+LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:16:17
+ |
+LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BinaryHeap<u8>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:19:17
+ |
+LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:21:17
+ |
+LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeSet<u8>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:24:17
+ |
+LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:26:17
+ |
+LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashSet<u8>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:29:17
+ |
+LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:31:17
+ |
+LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u8, u8>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:34:17
+ |
+LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u32, u32>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:35:17
+ |
+LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u8, [u8; 4]>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:37:17
+ |
+LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap<u32, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:38:17
+ |
+LL | let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u8, u8>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:41:17
+ |
+LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u32, u32>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:42:17
+ |
+LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u8, [u8; 4]>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:44:17
+ |
+LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap<u32, u32>` with mismatched layout is unsound
+ --> $DIR/transmute_collection.rs:45:17
+ |
+LL | let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs
new file mode 100644
index 000000000..806b2d77d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs
@@ -0,0 +1,25 @@
+#![warn(clippy::transmute_float_to_int)]
+
+fn float_to_int() {
+ let _: u32 = unsafe { std::mem::transmute(1f32) };
+ let _: i32 = unsafe { std::mem::transmute(1f32) };
+ let _: u64 = unsafe { std::mem::transmute(1f64) };
+ let _: i64 = unsafe { std::mem::transmute(1f64) };
+ let _: u64 = unsafe { std::mem::transmute(1.0) };
+ let _: u64 = unsafe { std::mem::transmute(-1.0) };
+}
+
+mod issue_5747 {
+ const VALUE32: i32 = unsafe { std::mem::transmute(1f32) };
+ const VALUE64: u64 = unsafe { std::mem::transmute(1f64) };
+
+ const fn to_bits_32(v: f32) -> u32 {
+ unsafe { std::mem::transmute(v) }
+ }
+
+ const fn to_bits_64(v: f64) -> i64 {
+ unsafe { std::mem::transmute(v) }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr
new file mode 100644
index 000000000..eb786bb39
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr
@@ -0,0 +1,40 @@
+error: transmute from a `f32` to a `u32`
+ --> $DIR/transmute_float_to_int.rs:4:27
+ |
+LL | let _: u32 = unsafe { std::mem::transmute(1f32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()`
+ |
+ = note: `-D clippy::transmute-float-to-int` implied by `-D warnings`
+
+error: transmute from a `f32` to a `i32`
+ --> $DIR/transmute_float_to_int.rs:5:27
+ |
+LL | let _: i32 = unsafe { std::mem::transmute(1f32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32`
+
+error: transmute from a `f64` to a `u64`
+ --> $DIR/transmute_float_to_int.rs:6:27
+ |
+LL | let _: u64 = unsafe { std::mem::transmute(1f64) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()`
+
+error: transmute from a `f64` to a `i64`
+ --> $DIR/transmute_float_to_int.rs:7:27
+ |
+LL | let _: i64 = unsafe { std::mem::transmute(1f64) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64`
+
+error: transmute from a `f64` to a `u64`
+ --> $DIR/transmute_float_to_int.rs:8:27
+ |
+LL | let _: u64 = unsafe { std::mem::transmute(1.0) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()`
+
+error: transmute from a `f64` to a `u64`
+ --> $DIR/transmute_float_to_int.rs:9:27
+ |
+LL | let _: u64 = unsafe { std::mem::transmute(-1.0) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
new file mode 100644
index 000000000..f06ffab5d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
@@ -0,0 +1,63 @@
+#![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(clippy::borrow_as_ptr)]
+
+// Make sure we can modify lifetimes, which is one of the recommended uses
+// of transmute
+
+// Make sure we can do static lifetime transmutes
+unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T {
+ std::mem::transmute::<&'a T, &'static T>(t)
+}
+
+// Make sure we can do non-static lifetime transmutes
+unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
+ std::mem::transmute::<&'a T, &'b T>(t)
+}
+
+struct LifetimeParam<'a> {
+ s: &'a str,
+}
+
+struct GenericParam<T> {
+ t: T,
+}
+
+fn transmute_ptr_to_ptr() {
+ let ptr = &1u32 as *const u32;
+ let mut_ptr = &mut 1u32 as *mut u32;
+ unsafe {
+ // pointer-to-pointer transmutes; bad
+ let _: *const f32 = std::mem::transmute(ptr);
+ let _: *mut f32 = std::mem::transmute(mut_ptr);
+ // ref-ref transmutes; bad
+ let _: &f32 = std::mem::transmute(&1u32);
+ let _: &f64 = std::mem::transmute(&1f32);
+ // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
+ // the same type
+ let _: &mut f32 = std::mem::transmute(&mut 1u32);
+ let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
+ }
+
+ // these are recommendations for solving the above; if these lint we need to update
+ // those suggestions
+ let _ = ptr as *const f32;
+ let _ = mut_ptr as *mut f32;
+ let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
+ let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
+
+ // transmute internal lifetimes, should not lint
+ let s = "hello world".to_owned();
+ let lp = LifetimeParam { s: &s };
+ let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
+ let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
+}
+
+// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959)
+const _: &() = {
+ struct Zst;
+ let zst = &Zst;
+
+ unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) }
+};
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
new file mode 100644
index 000000000..49a8a3347
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
@@ -0,0 +1,40 @@
+error: transmute from a pointer to a pointer
+ --> $DIR/transmute_ptr_to_ptr.rs:30:29
+ |
+LL | let _: *const f32 = std::mem::transmute(ptr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
+ |
+ = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
+
+error: transmute from a pointer to a pointer
+ --> $DIR/transmute_ptr_to_ptr.rs:31:27
+ |
+LL | let _: *mut f32 = std::mem::transmute(mut_ptr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
+
+error: transmute from a reference to a reference
+ --> $DIR/transmute_ptr_to_ptr.rs:33:23
+ |
+LL | let _: &f32 = std::mem::transmute(&1u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
+
+error: transmute from a reference to a reference
+ --> $DIR/transmute_ptr_to_ptr.rs:34:23
+ |
+LL | let _: &f64 = std::mem::transmute(&1f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)`
+
+error: transmute from a reference to a reference
+ --> $DIR/transmute_ptr_to_ptr.rs:37:27
+ |
+LL | let _: &mut f32 = std::mem::transmute(&mut 1u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
+
+error: transmute from a reference to a reference
+ --> $DIR/transmute_ptr_to_ptr.rs:38:37
+ |
+LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
new file mode 100644
index 000000000..e5fe9133f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
@@ -0,0 +1,78 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::match_single_binding)]
+
+unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
+ let _: &T = &*p;
+ let _: &T = &*p;
+
+ let _: &mut T = &mut *m;
+ let _: &mut T = &mut *m;
+
+ let _: &T = &*m;
+ let _: &T = &*m;
+
+ let _: &mut T = &mut *(p as *mut T);
+ let _ = &mut *(p as *mut T);
+
+ let _: &T = &*(o as *const T);
+ let _: &T = &*(o as *const T);
+
+ let _: &mut T = &mut *(om as *mut T);
+ let _: &mut T = &mut *(om as *mut T);
+
+ let _: &T = &*(om as *const T);
+ let _: &T = &*(om as *const T);
+}
+
+fn _issue1231() {
+ struct Foo<'a, T> {
+ bar: &'a T,
+ }
+
+ let raw = 42 as *const i32;
+ let _: &Foo<u8> = unsafe { &*raw.cast::<Foo<_>>() };
+
+ let _: &Foo<&u8> = unsafe { &*raw.cast::<Foo<&_>>() };
+
+ type Bar<'a> = &'a u8;
+ let raw = 42 as *const i32;
+ unsafe { &*(raw as *const u8) };
+}
+
+unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+ match 0 {
+ 0 => &*x.cast::<&u32>(),
+ 1 => &*y.cast::<&u32>(),
+ 2 => &*x.cast::<&'b u32>(),
+ _ => &*y.cast::<&'b u32>(),
+ }
+}
+
+unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+ #![clippy::msrv = "1.38"]
+ let a = 0u32;
+ let a = &a as *const u32;
+ let _: &u32 = &*a;
+ let _: &u32 = &*a.cast::<u32>();
+ match 0 {
+ 0 => &*x.cast::<&u32>(),
+ _ => &*x.cast::<&'b u32>(),
+ }
+}
+
+unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+ #![clippy::msrv = "1.37"]
+ let a = 0u32;
+ let a = &a as *const u32;
+ let _: &u32 = &*a;
+ let _: &u32 = &*(a as *const u32);
+ match 0 {
+ 0 => &*(x as *const () as *const &u32),
+ _ => &*(x as *const () as *const &'b u32),
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
new file mode 100644
index 000000000..fe49cdc32
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
@@ -0,0 +1,78 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::match_single_binding)]
+
+unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
+ let _: &T = std::mem::transmute(p);
+ let _: &T = &*p;
+
+ let _: &mut T = std::mem::transmute(m);
+ let _: &mut T = &mut *m;
+
+ let _: &T = std::mem::transmute(m);
+ let _: &T = &*m;
+
+ let _: &mut T = std::mem::transmute(p as *mut T);
+ let _ = &mut *(p as *mut T);
+
+ let _: &T = std::mem::transmute(o);
+ let _: &T = &*(o as *const T);
+
+ let _: &mut T = std::mem::transmute(om);
+ let _: &mut T = &mut *(om as *mut T);
+
+ let _: &T = std::mem::transmute(om);
+ let _: &T = &*(om as *const T);
+}
+
+fn _issue1231() {
+ struct Foo<'a, T> {
+ bar: &'a T,
+ }
+
+ let raw = 42 as *const i32;
+ let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
+
+ let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
+
+ type Bar<'a> = &'a u8;
+ let raw = 42 as *const i32;
+ unsafe { std::mem::transmute::<_, Bar>(raw) };
+}
+
+unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+ match 0 {
+ 0 => std::mem::transmute(x),
+ 1 => std::mem::transmute(y),
+ 2 => std::mem::transmute::<_, &&'b u32>(x),
+ _ => std::mem::transmute::<_, &&'b u32>(y),
+ }
+}
+
+unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+ #![clippy::msrv = "1.38"]
+ let a = 0u32;
+ let a = &a as *const u32;
+ let _: &u32 = std::mem::transmute(a);
+ let _: &u32 = std::mem::transmute::<_, &u32>(a);
+ match 0 {
+ 0 => std::mem::transmute(x),
+ _ => std::mem::transmute::<_, &&'b u32>(x),
+ }
+}
+
+unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+ #![clippy::msrv = "1.37"]
+ let a = 0u32;
+ let a = &a as *const u32;
+ let _: &u32 = std::mem::transmute(a);
+ let _: &u32 = std::mem::transmute::<_, &u32>(a);
+ match 0 {
+ 0 => std::mem::transmute(x),
+ _ => std::mem::transmute::<_, &&'b u32>(x),
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
new file mode 100644
index 000000000..2993e5e7b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
@@ -0,0 +1,136 @@
+error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
+ --> $DIR/transmute_ptr_to_ref.rs:8:17
+ |
+LL | let _: &T = std::mem::transmute(p);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
+ |
+ = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
+
+error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
+ --> $DIR/transmute_ptr_to_ref.rs:11:21
+ |
+LL | let _: &mut T = std::mem::transmute(m);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
+
+error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
+ --> $DIR/transmute_ptr_to_ref.rs:14:17
+ |
+LL | let _: &T = std::mem::transmute(m);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
+
+error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
+ --> $DIR/transmute_ptr_to_ref.rs:17:21
+ |
+LL | let _: &mut T = std::mem::transmute(p as *mut T);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
+
+error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
+ --> $DIR/transmute_ptr_to_ref.rs:20:17
+ |
+LL | let _: &T = std::mem::transmute(o);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
+
+error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
+ --> $DIR/transmute_ptr_to_ref.rs:23:21
+ |
+LL | let _: &mut T = std::mem::transmute(om);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
+
+error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
+ --> $DIR/transmute_ptr_to_ref.rs:26:17
+ |
+LL | let _: &T = std::mem::transmute(om);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
+
+error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<u8>`)
+ --> $DIR/transmute_ptr_to_ref.rs:36:32
+ |
+LL | let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
+
+error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<&u8>`)
+ --> $DIR/transmute_ptr_to_ref.rs:38:33
+ |
+LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()`
+
+error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
+ --> $DIR/transmute_ptr_to_ref.rs:42:14
+ |
+LL | unsafe { std::mem::transmute::<_, Bar>(raw) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:47:14
+ |
+LL | 0 => std::mem::transmute(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:48:14
+ |
+LL | 1 => std::mem::transmute(y),
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:49:14
+ |
+LL | 2 => std::mem::transmute::<_, &&'b u32>(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:50:14
+ |
+LL | _ => std::mem::transmute::<_, &&'b u32>(y),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:58:19
+ |
+LL | let _: &u32 = std::mem::transmute(a);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:59:19
+ |
+LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:61:14
+ |
+LL | 0 => std::mem::transmute(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:62:14
+ |
+LL | _ => std::mem::transmute::<_, &&'b u32>(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:70:19
+ |
+LL | let _: &u32 = std::mem::transmute(a);
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:71:19
+ |
+LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:73:14
+ |
+LL | 0 => std::mem::transmute(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+ --> $DIR/transmute_ptr_to_ref.rs:74:14
+ |
+LL | _ => std::mem::transmute::<_, &&'b u32>(x),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
new file mode 100644
index 000000000..ebcaa7a84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
@@ -0,0 +1,144 @@
+#![warn(clippy::transmute_undefined_repr)]
+#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)]
+
+use core::any::TypeId;
+use core::ffi::c_void;
+use core::mem::{size_of, transmute, MaybeUninit};
+
+fn value<T>() -> T {
+ unimplemented!()
+}
+
+struct Empty;
+struct Ty<T>(T);
+struct Ty2<T, U>(T, U);
+
+#[repr(C)]
+struct Ty2C<T, U>(T, U);
+
+fn main() {
+ unsafe {
+ let _: () = transmute(value::<Empty>());
+ let _: Empty = transmute(value::<()>());
+
+ let _: Ty<u32> = transmute(value::<u32>());
+ let _: Ty<u32> = transmute(value::<u32>());
+
+ let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
+ let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
+
+ let _: Ty2<u32, i32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
+ let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
+
+ let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
+
+ let _: Ty<&()> = transmute(value::<&()>());
+ let _: &() = transmute(value::<Ty<&()>>());
+
+ let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
+
+ let _: Ty<usize> = transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
+ let _: &Ty2<u32, i32> = transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
+
+ let _: Ty<[u8; 8]> = transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
+ let _: Ty2<u32, i32> = transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
+
+ // issue #8417
+ let _: Ty2C<Ty2<u32, i32>, ()> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
+ let _: Ty2<u32, i32> = transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
+
+ let _: &'static mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
+ let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
+ let _: *mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
+ let _: Box<Ty2<u32, u32>> = transmute(value::<*mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
+
+ let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
+ let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
+
+ let _: *const () = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
+ let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const ()>()); // Ok, reverse type erasure
+
+ let _: *const c_void = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
+ let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const c_void>()); // Ok, reverse type erasure
+
+ enum Erase {}
+ let _: *const Erase = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
+ let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase>()); // Ok, reverse type erasure
+
+ struct Erase2(
+ [u8; 0],
+ core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
+ );
+ let _: *const Erase2 = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
+ let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure
+
+ let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
+ let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure
+
+ let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
+ let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure
+
+ let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array
+ let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array
+
+ let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array
+ let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array
+
+ let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
+ let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
+
+ let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok
+ let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+ let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok
+ let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+ let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok
+ let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok
+
+ let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok
+ let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok
+
+ trait Trait {}
+ let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok
+ let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok
+
+ let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok
+ let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
+
+ let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
+ }
+}
+
+fn _with_generics<T: 'static, U: 'static>() {
+ if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
+ return;
+ }
+ unsafe {
+ let _: &u32 = transmute(value::<&T>()); // Ok
+ let _: &T = transmute(value::<&u32>()); // Ok
+
+ let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
+ let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok
+
+ let _: Ty<&u32> = transmute(value::<&T>()); // Ok
+ let _: Ty<&T> = transmute(value::<&u32>()); // Ok
+
+ let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
+ let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok
+
+ let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
+ let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok
+
+ let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
+ let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok
+
+ let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
+ let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
+
+ let _: *const u32 = transmute(value::<Box<T>>()); // Ok
+ let _: Box<T> = transmute(value::<*const u32>()); // Ok
+ }
+}
diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
new file mode 100644
index 000000000..28bfba6c7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
@@ -0,0 +1,80 @@
+error: transmute from `Ty2<u32, i32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:27:33
+ |
+LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
+
+error: transmute into `Ty2<u32, i32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:28:32
+ |
+LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:33:32
+ |
+LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:34:36
+ |
+LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:39:33
+ |
+LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:40:37
+ |
+LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:57:45
+ |
+LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:58:37
+ |
+LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:138:35
+ |
+LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Vec`) may have different layouts
+
+error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:139:35
+ |
+LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Vec`) may have different layouts
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
new file mode 100644
index 000000000..539239fc1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -0,0 +1,77 @@
+// run-rustfix
+#![warn(clippy::transmutes_expressible_as_ptr_casts)]
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
+// would otherwise be responsible for
+#![warn(clippy::useless_transmute)]
+#![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+
+use std::mem::{size_of, transmute};
+
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
+// valid, which we quote from below.
+fn main() {
+ // We should see an error message for each transmute, and no error messages for
+ // the casts, since the casts are the recommended fixes.
+
+ // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
+ let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
+ let ptr_i32 = usize::MAX as *const i32;
+
+ // e has type *T, U is *U_0, and either U_0: Sized ...
+ let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
+ let _ptr_i8 = ptr_i32 as *const i8;
+
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
+
+ // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
+ let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
+ let _ptr_to_unsized = slice_ptr as *const [u32];
+ // TODO: We could try testing vtable casts here too, but maybe
+ // we should wait until std::raw::TraitObject is stabilized?
+
+ // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
+ let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize };
+ let _usize_from_int_ptr = ptr_i32 as usize;
+
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
+
+ // e has type &[T; n] and U is *const T; array-ptr-cast
+ let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] };
+ let _array_ptr = array_ref as *const [i32; 4];
+
+ fn foo(_: usize) -> u8 {
+ 42
+ }
+
+ // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
+ let _usize_ptr_transmute = unsafe { foo as *const usize };
+ let _usize_ptr_transmute = foo as *const usize;
+
+ // e is a function pointer type and U is an integer; fptr-addr-cast
+ let _usize_from_fn_ptr_transmute = unsafe { foo as usize };
+ let _usize_from_fn_ptr = foo as *const usize;
+}
+
+// If a ref-to-ptr cast of this form where the pointer type points to a type other
+// than the referenced type, calling `CastCheck::do_check` has been observed to
+// cause an ICE error message. `do_check` is currently called inside the
+// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
+// currently prevent it from being called in these cases. This test is meant to
+// fail if the ordering of the checks ever changes enough to cause these cases to
+// fall through into `do_check`.
+fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
+ unsafe { in_param as *const [i32; 1] as *const u8 }
+}
+
+#[repr(C)]
+struct Single(u64);
+
+#[repr(C)]
+struct Pair(u32, u32);
+
+fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
+ assert_eq!(size_of::<Single>(), size_of::<Pair>());
+
+ unsafe { transmute::<Single, Pair>(in_param) }
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
new file mode 100644
index 000000000..b9e446dc8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -0,0 +1,77 @@
+// run-rustfix
+#![warn(clippy::transmutes_expressible_as_ptr_casts)]
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
+// would otherwise be responsible for
+#![warn(clippy::useless_transmute)]
+#![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+
+use std::mem::{size_of, transmute};
+
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
+// valid, which we quote from below.
+fn main() {
+ // We should see an error message for each transmute, and no error messages for
+ // the casts, since the casts are the recommended fixes.
+
+ // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
+ let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+ let ptr_i32 = usize::MAX as *const i32;
+
+ // e has type *T, U is *U_0, and either U_0: Sized ...
+ let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+ let _ptr_i8 = ptr_i32 as *const i8;
+
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
+
+ // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
+ let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
+ let _ptr_to_unsized = slice_ptr as *const [u32];
+ // TODO: We could try testing vtable casts here too, but maybe
+ // we should wait until std::raw::TraitObject is stabilized?
+
+ // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
+ let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+ let _usize_from_int_ptr = ptr_i32 as usize;
+
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
+
+ // e has type &[T; n] and U is *const T; array-ptr-cast
+ let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+ let _array_ptr = array_ref as *const [i32; 4];
+
+ fn foo(_: usize) -> u8 {
+ 42
+ }
+
+ // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
+ let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+ let _usize_ptr_transmute = foo as *const usize;
+
+ // e is a function pointer type and U is an integer; fptr-addr-cast
+ let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
+ let _usize_from_fn_ptr = foo as *const usize;
+}
+
+// If a ref-to-ptr cast of this form where the pointer type points to a type other
+// than the referenced type, calling `CastCheck::do_check` has been observed to
+// cause an ICE error message. `do_check` is currently called inside the
+// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
+// currently prevent it from being called in these cases. This test is meant to
+// fail if the ordering of the checks ever changes enough to cause these cases to
+// fall through into `do_check`.
+fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
+ unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
+}
+
+#[repr(C)]
+struct Single(u64);
+
+#[repr(C)]
+struct Pair(u32, u32);
+
+fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
+ assert_eq!(size_of::<Single>(), size_of::<Pair>());
+
+ unsafe { transmute::<Single, Pair>(in_param) }
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
new file mode 100644
index 000000000..de9418c8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -0,0 +1,56 @@
+error: transmute from an integer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39
+ |
+LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
+ |
+ = note: `-D clippy::useless-transmute` implied by `-D warnings`
+
+error: transmute from a pointer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38
+ |
+LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
+ |
+ = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
+
+error: transmute from a pointer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46
+ |
+LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u32]`
+
+error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50
+ |
+LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
+ |
+ = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
+
+error: transmute from a reference to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41
+ |
+LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
+
+error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41
+ |
+LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
+
+error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49
+ |
+LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
+
+error: transmute from a reference to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14
+ |
+LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs
new file mode 100644
index 000000000..ea3ee8edc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmuting_null.rs
@@ -0,0 +1,30 @@
+#![allow(dead_code)]
+#![warn(clippy::transmuting_null)]
+#![allow(clippy::zero_ptr)]
+#![allow(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::eq_op)]
+
+// Easy to lint because these only span one line.
+fn one_liners() {
+ unsafe {
+ let _: &u64 = std::mem::transmute(0 as *const u64);
+ let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
+ }
+}
+
+pub const ZPTR: *const usize = 0 as *const _;
+pub const NOT_ZPTR: *const usize = 1 as *const _;
+
+fn transmute_const() {
+ unsafe {
+ // Should raise a lint.
+ let _: &u64 = std::mem::transmute(ZPTR);
+ // Should NOT raise a lint.
+ let _: &u64 = std::mem::transmute(NOT_ZPTR);
+ }
+}
+
+fn main() {
+ one_liners();
+ transmute_const();
+}
diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr
new file mode 100644
index 000000000..1848fc249
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmuting_null.stderr
@@ -0,0 +1,22 @@
+error: transmuting a known null pointer into a reference
+ --> $DIR/transmuting_null.rs:10:23
+ |
+LL | let _: &u64 = std::mem::transmute(0 as *const u64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::transmuting-null` implied by `-D warnings`
+
+error: transmuting a known null pointer into a reference
+ --> $DIR/transmuting_null.rs:11:23
+ |
+LL | let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmuting a known null pointer into a reference
+ --> $DIR/transmuting_null.rs:21:23
+ |
+LL | let _: &u64 = std::mem::transmute(ZPTR);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.fixed b/src/tools/clippy/tests/ui/trim_split_whitespace.fixed
new file mode 100644
index 000000000..e4d352f73
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trim_split_whitespace.fixed
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomSplit {
+ #[allow(dead_code)]
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomTrim {
+ fn trim(self) -> Self {
+ self
+ }
+}
+
+fn main() {
+ // &str
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+
+ // String
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+
+ // Custom
+ let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str>
+ let s = DerefStr(" A B C ");
+ let _ = s.split_whitespace(); // should trigger lint
+
+ // Deref<Target=str> + custom impl
+ let s = DerefStrAndCustom(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str> + only custom split_ws() impl
+ let s = DerefStrAndCustomSplit(" A B C ");
+ let _ = s.split_whitespace(); // should trigger lint
+ // Expl: trim() is called on str (deref) and returns &str.
+ // Thus split_ws() is called on str as well and the custom impl on S is unused
+
+ // Deref<Target=str> + only custom trim() impl
+ let s = DerefStrAndCustomTrim(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.rs b/src/tools/clippy/tests/ui/trim_split_whitespace.rs
new file mode 100644
index 000000000..f98451a98
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trim_split_whitespace.rs
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomSplit {
+ #[allow(dead_code)]
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomTrim {
+ fn trim(self) -> Self {
+ self
+ }
+}
+
+fn main() {
+ // &str
+ let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+ let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+ let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+
+ // String
+ let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+
+ // Custom
+ let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str>
+ let s = DerefStr(" A B C ");
+ let _ = s.trim().split_whitespace(); // should trigger lint
+
+ // Deref<Target=str> + custom impl
+ let s = DerefStrAndCustom(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str> + only custom split_ws() impl
+ let s = DerefStrAndCustomSplit(" A B C ");
+ let _ = s.trim().split_whitespace(); // should trigger lint
+ // Expl: trim() is called on str (deref) and returns &str.
+ // Thus split_ws() is called on str as well and the custom impl on S is unused
+
+ // Deref<Target=str> + only custom trim() impl
+ let s = DerefStrAndCustomTrim(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.stderr b/src/tools/clippy/tests/ui/trim_split_whitespace.stderr
new file mode 100644
index 000000000..5ae7849e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trim_split_whitespace.stderr
@@ -0,0 +1,52 @@
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:62:23
+ |
+LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+ |
+ = note: `-D clippy::trim-split-whitespace` implied by `-D warnings`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:63:23
+ |
+LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:64:23
+ |
+LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:67:37
+ |
+LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:68:37
+ |
+LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:69:37
+ |
+LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:76:15
+ |
+LL | let _ = s.trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:84:15
+ |
+LL | let _ = s.trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
new file mode 100644
index 000000000..8f78f16a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
@@ -0,0 +1,168 @@
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![deny(clippy::trivially_copy_pass_by_ref)]
+#![allow(clippy::blacklisted_name, clippy::redundant_field_names)]
+
+#[derive(Copy, Clone)]
+struct Foo(u32);
+
+#[derive(Copy, Clone)]
+struct Bar([u8; 24]);
+
+#[derive(Copy, Clone)]
+pub struct Color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+ pub a: u8,
+}
+
+struct FooRef<'a> {
+ foo: &'a Foo,
+}
+
+type Baz = u32;
+
+fn good(a: &mut u32, b: u32, c: &Bar) {}
+
+fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
+ &foo.0
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
+ &foo.0
+}
+
+fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
+ FooRef { foo }
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
+ FooRef { foo }
+}
+
+fn bad(x: &u32, y: &Foo, z: &Baz) {}
+
+impl Foo {
+ fn good(self, a: &mut u32, b: u32, c: &Bar) {}
+
+ fn good2(&mut self) {}
+
+ fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+
+ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+
+ fn bad_issue7518(self, other: &Self) {}
+}
+
+impl AsRef<u32> for Foo {
+ fn as_ref(&self) -> &u32 {
+ &self.0
+ }
+}
+
+impl Bar {
+ fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
+
+ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+}
+
+trait MyTrait {
+ fn trait_method(&self, _foo: &Foo);
+}
+
+pub trait MyTrait2 {
+ fn trait_method2(&self, _color: &Color);
+}
+
+impl MyTrait for Foo {
+ fn trait_method(&self, _foo: &Foo) {
+ unimplemented!()
+ }
+}
+
+#[allow(unused_variables)]
+mod issue3992 {
+ pub trait A {
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ fn a(b: &u16) {}
+ }
+
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn c(d: &u16) {}
+}
+
+mod issue5876 {
+ // Don't lint here as it is always inlined
+ #[inline(always)]
+ fn foo_always(x: &i32) {
+ println!("{}", x);
+ }
+
+ #[inline(never)]
+ fn foo_never(x: &i32) {
+ println!("{}", x);
+ }
+
+ #[inline]
+ fn foo(x: &i32) {
+ println!("{}", x);
+ }
+}
+
+fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
+ Some(x)
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
+ Some(x)
+}
+
+fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
+ if true { x } else { y }
+}
+
+async fn _async_implicit(x: &u32) -> &u32 {
+ x
+}
+
+#[allow(clippy::needless_lifetimes)]
+async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 {
+ x
+}
+
+fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
+ y
+}
+
+fn _return_ptr(x: &u32) -> *const u32 {
+ x
+}
+
+fn _return_field_ptr(x: &(u32, u32)) -> *const u32 {
+ &x.0
+}
+
+fn _return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
+ core::ptr::addr_of!(x.0)
+}
+
+fn main() {
+ let (mut foo, bar) = (Foo(0), Bar([0; 24]));
+ let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
+ good(&mut a, b, &c);
+ good_return_implicit_lt_ref(&y);
+ good_return_explicit_lt_ref(&y);
+ bad(&x, &y, &z);
+ foo.good(&mut a, b, &c);
+ foo.good2();
+ foo.bad(&x, &y, &z);
+ Foo::bad2(&x, &y, &z);
+ bar.good(&mut a, b, &c);
+ Bar::bad2(&x, &y, &z);
+ foo.as_ref();
+}
diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
new file mode 100644
index 000000000..66ecb3d8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
@@ -0,0 +1,116 @@
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:11
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+ |
+note: the lint level is defined here
+ --> $DIR/trivially_copy_pass_by_ref.rs:4:9
+ |
+LL | #![deny(clippy::trivially_copy_pass_by_ref)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:20
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:29
+ |
+LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:12
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^^ help: consider passing by value instead: `self`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:22
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:31
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:40
+ |
+LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:16
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:25
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:34
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:58:35
+ |
+LL | fn bad_issue7518(self, other: &Self) {}
+ | ^^^^^ help: consider passing by value instead: `Self`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:16
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `u32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:25
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:34
+ |
+LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
+ | ^^^^ help: consider passing by value instead: `Baz`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:74:34
+ |
+LL | fn trait_method(&self, _foo: &Foo);
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:106:21
+ |
+LL | fn foo_never(x: &i32) {
+ | ^^^^ help: consider passing by value instead: `i32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:111:15
+ |
+LL | fn foo(x: &i32) {
+ | ^^^^ help: consider passing by value instead: `i32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/trivially_copy_pass_by_ref.rs:138:37
+ |
+LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
+ | ^^^^^^^ help: consider passing by value instead: `u32`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed
new file mode 100644
index 000000000..264194419
--- /dev/null
+++ b/src/tools/clippy/tests/ui/try_err.fixed
@@ -0,0 +1,170 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![deny(clippy::try_err)]
+#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
+
+#[macro_use]
+extern crate macro_rules;
+
+use std::io;
+use std::task::Poll;
+
+// Tests that a simple case works
+// Should flag `Err(err)?`
+pub fn basic_test() -> Result<i32, i32> {
+ let err: i32 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ return Err(err);
+ }
+ Ok(0)
+}
+
+// Tests that `.into()` is added when appropriate
+pub fn into_test() -> Result<i32, i32> {
+ let err: u8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ return Err(err.into());
+ }
+ Ok(0)
+}
+
+// Tests that tries in general don't trigger the error
+pub fn negative_test() -> Result<i32, i32> {
+ Ok(nested_error()? + 1)
+}
+
+// Tests that `.into()` isn't added when the error type
+// matches the surrounding closure's return type, even
+// when it doesn't match the surrounding function's.
+pub fn closure_matches_test() -> Result<i32, i32> {
+ let res: Result<i32, i8> = Some(1)
+ .into_iter()
+ .map(|i| {
+ let err: i8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ return Err(err);
+ }
+ Ok(i)
+ })
+ .next()
+ .unwrap();
+
+ Ok(res?)
+}
+
+// Tests that `.into()` isn't added when the error type
+// doesn't match the surrounding closure's return type.
+pub fn closure_into_test() -> Result<i32, i32> {
+ let res: Result<i32, i16> = Some(1)
+ .into_iter()
+ .map(|i| {
+ let err: i8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ return Err(err.into());
+ }
+ Ok(i)
+ })
+ .next()
+ .unwrap();
+
+ Ok(res?)
+}
+
+fn nested_error() -> Result<i32, i32> {
+ Ok(1)
+}
+
+// Bad suggestion when in macro (see #6242)
+macro_rules! try_validation {
+ ($e: expr) => {{
+ match $e {
+ Ok(_) => 0,
+ Err(_) => return Err(1),
+ }
+ }};
+}
+
+macro_rules! ret_one {
+ () => {
+ 1
+ };
+}
+
+macro_rules! try_validation_in_macro {
+ ($e: expr) => {{
+ match $e {
+ Ok(_) => 0,
+ Err(_) => return Err(ret_one!()),
+ }
+ }};
+}
+
+fn calling_macro() -> Result<i32, i32> {
+ // macro
+ try_validation!(Ok::<_, i32>(5));
+ // `Err` arg is another macro
+ try_validation_in_macro!(Ok::<_, i32>(5));
+ Ok(5)
+}
+
+fn main() {
+ basic_test().unwrap();
+ into_test().unwrap();
+ negative_test().unwrap();
+ closure_matches_test().unwrap();
+ closure_into_test().unwrap();
+ calling_macro().unwrap();
+
+ // We don't want to lint in external macros
+ try_err!();
+}
+
+macro_rules! bar {
+ () => {
+ String::from("aasdfasdfasdfa")
+ };
+}
+
+macro_rules! foo {
+ () => {
+ bar!()
+ };
+}
+
+pub fn macro_inside(fail: bool) -> Result<i32, String> {
+ if fail {
+ return Err(foo!());
+ }
+ Ok(0)
+}
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+ if n == 0 {
+ return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))
+ } else if n == 1 {
+ return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))
+ };
+
+ Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+ if !ready {
+ return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))
+ }
+
+ Poll::Ready(None)
+}
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+ if x {
+ return Err(42);
+ }
+ Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs
new file mode 100644
index 000000000..bc6979bf4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/try_err.rs
@@ -0,0 +1,170 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![deny(clippy::try_err)]
+#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
+
+#[macro_use]
+extern crate macro_rules;
+
+use std::io;
+use std::task::Poll;
+
+// Tests that a simple case works
+// Should flag `Err(err)?`
+pub fn basic_test() -> Result<i32, i32> {
+ let err: i32 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ Err(err)?;
+ }
+ Ok(0)
+}
+
+// Tests that `.into()` is added when appropriate
+pub fn into_test() -> Result<i32, i32> {
+ let err: u8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ Err(err)?;
+ }
+ Ok(0)
+}
+
+// Tests that tries in general don't trigger the error
+pub fn negative_test() -> Result<i32, i32> {
+ Ok(nested_error()? + 1)
+}
+
+// Tests that `.into()` isn't added when the error type
+// matches the surrounding closure's return type, even
+// when it doesn't match the surrounding function's.
+pub fn closure_matches_test() -> Result<i32, i32> {
+ let res: Result<i32, i8> = Some(1)
+ .into_iter()
+ .map(|i| {
+ let err: i8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ Err(err)?;
+ }
+ Ok(i)
+ })
+ .next()
+ .unwrap();
+
+ Ok(res?)
+}
+
+// Tests that `.into()` isn't added when the error type
+// doesn't match the surrounding closure's return type.
+pub fn closure_into_test() -> Result<i32, i32> {
+ let res: Result<i32, i16> = Some(1)
+ .into_iter()
+ .map(|i| {
+ let err: i8 = 1;
+ // To avoid warnings during rustfix
+ if true {
+ Err(err)?;
+ }
+ Ok(i)
+ })
+ .next()
+ .unwrap();
+
+ Ok(res?)
+}
+
+fn nested_error() -> Result<i32, i32> {
+ Ok(1)
+}
+
+// Bad suggestion when in macro (see #6242)
+macro_rules! try_validation {
+ ($e: expr) => {{
+ match $e {
+ Ok(_) => 0,
+ Err(_) => Err(1)?,
+ }
+ }};
+}
+
+macro_rules! ret_one {
+ () => {
+ 1
+ };
+}
+
+macro_rules! try_validation_in_macro {
+ ($e: expr) => {{
+ match $e {
+ Ok(_) => 0,
+ Err(_) => Err(ret_one!())?,
+ }
+ }};
+}
+
+fn calling_macro() -> Result<i32, i32> {
+ // macro
+ try_validation!(Ok::<_, i32>(5));
+ // `Err` arg is another macro
+ try_validation_in_macro!(Ok::<_, i32>(5));
+ Ok(5)
+}
+
+fn main() {
+ basic_test().unwrap();
+ into_test().unwrap();
+ negative_test().unwrap();
+ closure_matches_test().unwrap();
+ closure_into_test().unwrap();
+ calling_macro().unwrap();
+
+ // We don't want to lint in external macros
+ try_err!();
+}
+
+macro_rules! bar {
+ () => {
+ String::from("aasdfasdfasdfa")
+ };
+}
+
+macro_rules! foo {
+ () => {
+ bar!()
+ };
+}
+
+pub fn macro_inside(fail: bool) -> Result<i32, String> {
+ if fail {
+ Err(foo!())?;
+ }
+ Ok(0)
+}
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+ if n == 0 {
+ Err(io::ErrorKind::WriteZero)?
+ } else if n == 1 {
+ Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+ };
+
+ Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+ if !ready {
+ Err(io::ErrorKind::NotFound)?
+ }
+
+ Poll::Ready(None)
+}
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+ if x {
+ return Err(42)?;
+ }
+ Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr
new file mode 100644
index 000000000..0cb1328fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/try_err.stderr
@@ -0,0 +1,84 @@
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:19:9
+ |
+LL | Err(err)?;
+ | ^^^^^^^^^ help: try this: `return Err(err)`
+ |
+note: the lint level is defined here
+ --> $DIR/try_err.rs:4:9
+ |
+LL | #![deny(clippy::try_err)]
+ | ^^^^^^^^^^^^^^^
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:29:9
+ |
+LL | Err(err)?;
+ | ^^^^^^^^^ help: try this: `return Err(err.into())`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:49:17
+ |
+LL | Err(err)?;
+ | ^^^^^^^^^ help: try this: `return Err(err)`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:68:17
+ |
+LL | Err(err)?;
+ | ^^^^^^^^^ help: try this: `return Err(err.into())`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:87:23
+ |
+LL | Err(_) => Err(1)?,
+ | ^^^^^^^ help: try this: `return Err(1)`
+...
+LL | try_validation!(Ok::<_, i32>(5));
+ | -------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `try_validation` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:102:23
+ |
+LL | Err(_) => Err(ret_one!())?,
+ | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())`
+...
+LL | try_validation_in_macro!(Ok::<_, i32>(5));
+ | ----------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `try_validation_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:141:9
+ |
+LL | Err(foo!())?;
+ | ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:148:9
+ |
+LL | Err(io::ErrorKind::WriteZero)?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:150:9
+ |
+LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:158:9
+ |
+LL | Err(io::ErrorKind::NotFound)?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:167:16
+ |
+LL | return Err(42)?;
+ | ^^^^^^^^ help: try this: `Err(42)`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ty_fn_sig.rs b/src/tools/clippy/tests/ui/ty_fn_sig.rs
new file mode 100644
index 000000000..9e2753dcb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ty_fn_sig.rs
@@ -0,0 +1,14 @@
+// Regression test
+
+pub fn retry<F: Fn()>(f: F) {
+ for _i in 0.. {
+ f();
+ }
+}
+
+fn main() {
+ for y in 0..4 {
+ let func = || ();
+ func();
+ }
+}
diff --git a/src/tools/clippy/tests/ui/type_complexity.rs b/src/tools/clippy/tests/ui/type_complexity.rs
new file mode 100644
index 000000000..86a7bd7b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/type_complexity.rs
@@ -0,0 +1,69 @@
+#![warn(clippy::all)]
+#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)]
+#![feature(associated_type_defaults)]
+
+type Alias = Vec<Vec<Box<(u32, u32, u32, u32)>>>; // no warning here
+
+const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+
+struct S {
+ f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
+}
+
+struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
+
+enum E {
+ Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
+ Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
+}
+
+impl S {
+ const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+ fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+}
+
+trait T {
+ const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+ type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+ fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
+ fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+}
+
+// Should not warn since there is likely no way to simplify this (#1013)
+impl T for () {
+ const A: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
+
+ type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+
+ fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+}
+
+fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
+ vec![]
+}
+
+fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+
+fn test3() {
+ let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
+}
+
+#[repr(C)]
+struct D {
+ // should not warn, since we don't have control over the signature (#3222)
+ test4: extern "C" fn(
+ itself: &D,
+ a: usize,
+ b: usize,
+ c: usize,
+ d: usize,
+ e: usize,
+ f: usize,
+ g: usize,
+ h: usize,
+ i: usize,
+ ),
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/type_complexity.stderr b/src/tools/clippy/tests/ui/type_complexity.stderr
new file mode 100644
index 000000000..9da7edb1c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/type_complexity.stderr
@@ -0,0 +1,94 @@
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:7:12
+ |
+LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::type-complexity` implied by `-D warnings`
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:8:12
+ |
+LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:11:8
+ |
+LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:14:11
+ |
+LL | struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:17:11
+ |
+LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:18:17
+ |
+LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:22:14
+ |
+LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:23:30
+ |
+LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:27:14
+ |
+LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:28:14
+ |
+LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:29:25
+ |
+LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:30:29
+ |
+LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:42:15
+ |
+LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:46:14
+ |
+LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: very complex type used. Consider factoring parts into `type` definitions
+ --> $DIR/type_complexity.rs:49:13
+ |
+LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
new file mode 100644
index 000000000..2eca1f470
--- /dev/null
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
@@ -0,0 +1,97 @@
+#![deny(clippy::type_repetition_in_bounds)]
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+pub fn foo<T>(_t: T)
+where
+ T: Copy,
+ T: Clone,
+{
+ unimplemented!();
+}
+
+pub fn bar<T, U>(_t: T, _u: U)
+where
+ T: Copy,
+ U: Clone,
+{
+ unimplemented!();
+}
+
+// Threshold test (see #4380)
+trait LintBounds
+where
+ Self: Clone,
+ Self: Copy + Default + Ord,
+ Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
+ Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
+{
+}
+
+trait LotsOfBounds
+where
+ Self: Clone + Copy + Default + Ord,
+ Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
+ Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
+{
+}
+
+// Generic distinction (see #4323)
+mod issue4323 {
+ pub struct Foo<A>(A);
+ pub struct Bar<A, B> {
+ a: Foo<A>,
+ b: Foo<B>,
+ }
+
+ impl<A, B> Unpin for Bar<A, B>
+ where
+ Foo<A>: Unpin,
+ Foo<B>: Unpin,
+ {
+ }
+}
+
+// Extern macros shouldn't lint (see #4326)
+extern crate serde;
+mod issue4326 {
+ use serde::{Deserialize, Serialize};
+
+ trait Foo {}
+ impl Foo for String {}
+
+ #[derive(Debug, Serialize, Deserialize)]
+ struct Bar<S>
+ where
+ S: Foo,
+ {
+ foo: S,
+ }
+}
+
+// Issue #7360
+struct Foo<T, U>
+where
+ T: Clone,
+ U: Clone,
+{
+ t: T,
+ u: U,
+}
+
+// Check for the `?` in `?Sized`
+pub fn f<T: ?Sized>()
+where
+ T: Clone,
+{
+}
+pub fn g<T: Clone>()
+where
+ T: ?Sized,
+{
+}
+
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
new file mode 100644
index 000000000..1d8871481
--- /dev/null
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
@@ -0,0 +1,39 @@
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:8:5
+ |
+LL | T: Clone,
+ | ^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/type_repetition_in_bounds.rs:1:9
+ |
+LL | #![deny(clippy::type_repetition_in_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider combining the bounds: `T: Copy + Clone`
+
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:25:5
+ |
+LL | Self: Copy + Default + Ord,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
+
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:85:5
+ |
+LL | T: Clone,
+ | ^^^^^^^^
+ |
+ = help: consider combining the bounds: `T: ?Sized + Clone`
+
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:90:5
+ |
+LL | T: ?Sized,
+ | ^^^^^^^^^
+ |
+ = help: consider combining the bounds: `T: Clone + ?Sized`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/types.fixed b/src/tools/clippy/tests/ui/types.fixed
new file mode 100644
index 000000000..417da42ed
--- /dev/null
+++ b/src/tools/clippy/tests/ui/types.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+#![warn(clippy::cast_lossless)]
+
+// should not warn on lossy casting in constant types
+// because not supported yet
+const C: i32 = 42;
+const C_I64: i64 = C as i64;
+
+fn main() {
+ // should suggest i64::from(c)
+ let c: i32 = 42;
+ let c_i64: i64 = i64::from(c);
+}
diff --git a/src/tools/clippy/tests/ui/types.rs b/src/tools/clippy/tests/ui/types.rs
new file mode 100644
index 000000000..b16e9e538
--- /dev/null
+++ b/src/tools/clippy/tests/ui/types.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+#![warn(clippy::cast_lossless)]
+
+// should not warn on lossy casting in constant types
+// because not supported yet
+const C: i32 = 42;
+const C_I64: i64 = C as i64;
+
+fn main() {
+ // should suggest i64::from(c)
+ let c: i32 = 42;
+ let c_i64: i64 = c as i64;
+}
diff --git a/src/tools/clippy/tests/ui/types.stderr b/src/tools/clippy/tests/ui/types.stderr
new file mode 100644
index 000000000..59c3e05a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/types.stderr
@@ -0,0 +1,10 @@
+error: casting `i32` to `i64` may become silently lossy if you later change the type
+ --> $DIR/types.rs:14:22
+ |
+LL | let c_i64: i64 = c as i64;
+ | ^^^^^^^^ help: try: `i64::from(c)`
+ |
+ = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
new file mode 100644
index 000000000..08aee4332
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
@@ -0,0 +1,493 @@
+// aux-build:proc_macro_unsafe.rs
+
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
+
+extern crate proc_macro_unsafe;
+
+// Valid comments
+
+fn nested_local() {
+ let _ = {
+ let _ = {
+ // SAFETY:
+ let _ = unsafe {};
+ };
+ };
+}
+
+fn deep_nest() {
+ let _ = {
+ let _ = {
+ // SAFETY:
+ let _ = unsafe {};
+
+ // Safety:
+ unsafe {};
+
+ let _ = {
+ let _ = {
+ let _ = {
+ let _ = {
+ let _ = {
+ // Safety:
+ let _ = unsafe {};
+
+ // SAFETY:
+ unsafe {};
+ };
+ };
+ };
+
+ // Safety:
+ unsafe {};
+ };
+ };
+ };
+
+ // Safety:
+ unsafe {};
+ };
+
+ // SAFETY:
+ unsafe {};
+}
+
+fn local_tuple_expression() {
+ // Safety:
+ let _ = (42, unsafe {});
+}
+
+fn line_comment() {
+ // Safety:
+ unsafe {}
+}
+
+fn line_comment_newlines() {
+ // SAFETY:
+
+ unsafe {}
+}
+
+fn line_comment_empty() {
+ // Safety:
+ //
+ //
+ //
+ unsafe {}
+}
+
+fn line_comment_with_extras() {
+ // This is a description
+ // Safety:
+ unsafe {}
+}
+
+fn block_comment() {
+ /* Safety: */
+ unsafe {}
+}
+
+fn block_comment_newlines() {
+ /* SAFETY: */
+
+ unsafe {}
+}
+
+fn block_comment_with_extras() {
+ /* This is a description
+ * SAFETY:
+ */
+ unsafe {}
+}
+
+fn block_comment_terminator_same_line() {
+ /* This is a description
+ * Safety: */
+ unsafe {}
+}
+
+fn buried_safety() {
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+ // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+ // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ // laborum. Safety:
+ // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
+ // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
+ // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
+ // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
+ unsafe {}
+}
+
+fn safety_with_prepended_text() {
+ // This is a test. safety:
+ unsafe {}
+}
+
+fn local_line_comment() {
+ // Safety:
+ let _ = unsafe {};
+}
+
+fn local_block_comment() {
+ /* SAFETY: */
+ let _ = unsafe {};
+}
+
+fn comment_array() {
+ // Safety:
+ let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+}
+
+fn comment_tuple() {
+ // sAFETY:
+ let _ = (42, unsafe {}, "test", unsafe {});
+}
+
+fn comment_unary() {
+ // SAFETY:
+ let _ = *unsafe { &42 };
+}
+
+#[allow(clippy::match_single_binding)]
+fn comment_match() {
+ // SAFETY:
+ let _ = match unsafe {} {
+ _ => {},
+ };
+}
+
+fn comment_addr_of() {
+ // Safety:
+ let _ = &unsafe {};
+}
+
+fn comment_repeat() {
+ // Safety:
+ let _ = [unsafe {}; 5];
+}
+
+fn comment_macro_call() {
+ macro_rules! t {
+ ($b:expr) => {
+ $b
+ };
+ }
+
+ t!(
+ // SAFETY:
+ unsafe {}
+ );
+}
+
+fn comment_macro_def() {
+ macro_rules! t {
+ () => {
+ // Safety:
+ unsafe {}
+ };
+ }
+
+ t!();
+}
+
+fn non_ascii_comment() {
+ // ॐ᧻໒ SaFeTy: ௵∰
+ unsafe {};
+}
+
+fn local_commented_block() {
+ let _ =
+ // safety:
+ unsafe {};
+}
+
+fn local_nest() {
+ // safety:
+ let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
+}
+
+fn in_fn_call(x: *const u32) {
+ fn f(x: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x });
+}
+
+fn multi_in_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x }, unsafe { *x });
+}
+
+fn in_multiline_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ f(
+ // Safety: reason
+ unsafe { *x },
+ 0,
+ );
+}
+
+fn in_macro_call(x: *const u32) {
+ // Safety: reason
+ println!("{}", unsafe { *x });
+}
+
+fn in_multiline_macro_call(x: *const u32) {
+ println!(
+ "{}",
+ // Safety: reason
+ unsafe { *x },
+ );
+}
+
+fn from_proc_macro() {
+ proc_macro_unsafe::unsafe_block!(token);
+}
+
+fn in_closure(x: *const u32) {
+ // Safety: reason
+ let _ = || unsafe { *x };
+}
+
+// Invalid comments
+
+#[rustfmt::skip]
+fn inline_block_comment() {
+ /* Safety: */ unsafe {}
+}
+
+fn no_comment() {
+ unsafe {}
+}
+
+fn no_comment_array() {
+ let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+}
+
+fn no_comment_tuple() {
+ let _ = (42, unsafe {}, "test", unsafe {});
+}
+
+fn no_comment_unary() {
+ let _ = *unsafe { &42 };
+}
+
+#[allow(clippy::match_single_binding)]
+fn no_comment_match() {
+ let _ = match unsafe {} {
+ _ => {},
+ };
+}
+
+fn no_comment_addr_of() {
+ let _ = &unsafe {};
+}
+
+fn no_comment_repeat() {
+ let _ = [unsafe {}; 5];
+}
+
+fn local_no_comment() {
+ let _ = unsafe {};
+}
+
+fn no_comment_macro_call() {
+ macro_rules! t {
+ ($b:expr) => {
+ $b
+ };
+ }
+
+ t!(unsafe {});
+}
+
+fn no_comment_macro_def() {
+ macro_rules! t {
+ () => {
+ unsafe {}
+ };
+ }
+
+ t!();
+}
+
+fn trailing_comment() {
+ unsafe {} // SAFETY:
+}
+
+fn internal_comment() {
+ unsafe {
+ // SAFETY:
+ }
+}
+
+fn interference() {
+ // SAFETY
+
+ let _ = 42;
+
+ unsafe {};
+}
+
+pub fn print_binary_tree() {
+ println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+}
+
+mod unsafe_impl_smoke_test {
+ unsafe trait A {}
+
+ // error: no safety comment
+ unsafe impl A for () {}
+
+ // Safety: ok
+ unsafe impl A for (i32) {}
+
+ mod sub_mod {
+ // error:
+ unsafe impl B for (u32) {}
+ unsafe trait B {}
+ }
+
+ #[rustfmt::skip]
+ mod sub_mod2 {
+ //
+ // SAFETY: ok
+ //
+
+ unsafe impl B for (u32) {}
+ unsafe trait B {}
+ }
+}
+
+mod unsafe_impl_from_macro {
+ unsafe trait T {}
+
+ // error
+ macro_rules! no_safety_comment {
+ ($t:ty) => {
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ no_safety_comment!(());
+
+ // ok
+ macro_rules! with_safety_comment {
+ ($t:ty) => {
+ // SAFETY:
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ with_safety_comment!((i32));
+}
+
+mod unsafe_impl_macro_and_not_macro {
+ unsafe trait T {}
+
+ // error
+ macro_rules! no_safety_comment {
+ ($t:ty) => {
+ unsafe impl T for $t {}
+ };
+ }
+
+ // ok
+ no_safety_comment!(());
+
+ // error
+ unsafe impl T for (i32) {}
+
+ // ok
+ no_safety_comment!(u32);
+
+ // error
+ unsafe impl T for (bool) {}
+}
+
+#[rustfmt::skip]
+mod unsafe_impl_valid_comment {
+ unsafe trait SaFety {}
+ // SaFety:
+ unsafe impl SaFety for () {}
+
+ unsafe trait MultiLineComment {}
+ // The following impl is safe
+ // ...
+ // Safety: reason
+ unsafe impl MultiLineComment for () {}
+
+ unsafe trait NoAscii {}
+ // 安全 SAFETY: 以下のコードは安全です
+ unsafe impl NoAscii for () {}
+
+ unsafe trait InlineAndPrecedingComment {}
+ // SAFETY:
+ /* comment */ unsafe impl InlineAndPrecedingComment for () {}
+
+ unsafe trait BuriedSafety {}
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+ // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+ // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ // laborum. Safety:
+ // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
+ // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
+ // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
+ // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
+ unsafe impl BuriedSafety for () {}
+
+ unsafe trait MultiLineBlockComment {}
+ /* This is a description
+ * Safety: */
+ unsafe impl MultiLineBlockComment for () {}
+}
+
+#[rustfmt::skip]
+mod unsafe_impl_invalid_comment {
+ unsafe trait NoComment {}
+
+ unsafe impl NoComment for () {}
+
+ unsafe trait InlineComment {}
+
+ /* SAFETY: */ unsafe impl InlineComment for () {}
+
+ unsafe trait TrailingComment {}
+
+ unsafe impl TrailingComment for () {} // SAFETY:
+
+ unsafe trait Interference {}
+ // SAFETY:
+ const BIG_NUMBER: i32 = 1000000;
+ unsafe impl Interference for () {}
+}
+
+unsafe trait ImplInFn {}
+
+fn impl_in_fn() {
+ // error
+ unsafe impl ImplInFn for () {}
+
+ // SAFETY: ok
+ unsafe impl ImplInFn for (i32) {}
+}
+
+unsafe trait CrateRoot {}
+
+// error
+unsafe impl CrateRoot for () {}
+
+// SAFETY: ok
+unsafe impl CrateRoot for (i32) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
new file mode 100644
index 000000000..c6a212744
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
@@ -0,0 +1,267 @@
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:262:19
+ |
+LL | /* Safety: */ unsafe {}
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:266:5
+ |
+LL | unsafe {}
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:270:14
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:270:29
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:270:48
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:274:18
+ |
+LL | let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:274:37
+ |
+LL | let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:278:14
+ |
+LL | let _ = *unsafe { &42 };
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:283:19
+ |
+LL | let _ = match unsafe {} {
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:289:14
+ |
+LL | let _ = &unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:293:14
+ |
+LL | let _ = [unsafe {}; 5];
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:297:13
+ |
+LL | let _ = unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:307:8
+ |
+LL | t!(unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:313:13
+ |
+LL | unsafe {}
+ | ^^^^^^^^^
+...
+LL | t!();
+ | ---- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:321:5
+ |
+LL | unsafe {} // SAFETY:
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:325:5
+ |
+LL | unsafe {
+ | ^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:335:5
+ |
+LL | unsafe {};
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:339:20
+ |
+LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:346:5
+ |
+LL | unsafe impl A for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:353:9
+ |
+LL | unsafe impl B for (u32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:374:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(());
+ | ---------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:399:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(());
+ | ---------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:407:5
+ |
+LL | unsafe impl T for (i32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:399:13
+ |
+LL | unsafe impl T for $t {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | no_safety_comment!(u32);
+ | ----------------------- in this macro invocation
+ |
+ = help: consider adding a safety comment on the preceding line
+ = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:413:5
+ |
+LL | unsafe impl T for (bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:459:5
+ |
+LL | unsafe impl NoComment for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:463:19
+ |
+LL | /* SAFETY: */ unsafe impl InlineComment for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:467:5
+ |
+LL | unsafe impl TrailingComment for () {} // SAFETY:
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:472:5
+ |
+LL | unsafe impl Interference for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:479:5
+ |
+LL | unsafe impl ImplInFn for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe impl missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:488:1
+ |
+LL | unsafe impl CrateRoot for () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: aborting due to 31 previous errors
+
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.rs b/src/tools/clippy/tests/ui/undropped_manually_drops.rs
new file mode 100644
index 000000000..f4cfc92e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undropped_manually_drops.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::undropped_manually_drops)]
+
+struct S;
+
+fn main() {
+ let f = std::mem::drop;
+ let g = std::mem::ManuallyDrop::drop;
+ let mut manual1 = std::mem::ManuallyDrop::new(S);
+ let mut manual2 = std::mem::ManuallyDrop::new(S);
+ let mut manual3 = std::mem::ManuallyDrop::new(S);
+ let mut manual4 = std::mem::ManuallyDrop::new(S);
+
+ // These lines will not drop `S` and should be linted
+ drop(std::mem::ManuallyDrop::new(S));
+ drop(manual1);
+
+ // FIXME: this line is not linted, though it should be
+ f(manual2);
+
+ // These lines will drop `S` and should be okay.
+ unsafe {
+ std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
+ std::mem::ManuallyDrop::drop(&mut manual3);
+ g(&mut manual4);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
new file mode 100644
index 000000000..2ac0fe986
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
@@ -0,0 +1,19 @@
+error: the inner value of this ManuallyDrop will not be dropped
+ --> $DIR/undropped_manually_drops.rs:14:5
+ |
+LL | drop(std::mem::ManuallyDrop::new(S));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::undropped-manually-drops` implied by `-D warnings`
+ = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
+
+error: the inner value of this ManuallyDrop will not be dropped
+ --> $DIR/undropped_manually_drops.rs:15:5
+ |
+LL | drop(manual1);
+ | ^^^^^^^^^^^^^
+ |
+ = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unicode.fixed b/src/tools/clippy/tests/ui/unicode.fixed
new file mode 100644
index 000000000..328cda369
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unicode.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+#[warn(clippy::invisible_characters)]
+fn zero() {
+ print!("Here >\u{200B}< is a ZWS, and \u{200B}another");
+ print!("This\u{200B}is\u{200B}fine");
+ print!("Here >\u{AD}< is a SHY, and \u{AD}another");
+ print!("This\u{ad}is\u{ad}fine");
+ print!("Here >\u{2060}< is a WJ, and \u{2060}another");
+ print!("This\u{2060}is\u{2060}fine");
+}
+
+#[warn(clippy::unicode_not_nfc)]
+fn canon() {
+ print!("̀àh?");
+ print!("a\u{0300}h?"); // also ok
+}
+
+#[warn(clippy::non_ascii_literal)]
+fn uni() {
+ print!("\u{dc}ben!");
+ print!("\u{DC}ben!"); // this is ok
+}
+
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+ const _EMPTY_BLOCK: char = '\u{25b1}';
+ const _FULL_BLOCK: char = '\u{25b0}';
+}
+
+fn main() {
+ zero();
+ uni();
+ canon();
+ single_quote();
+}
diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs
new file mode 100644
index 000000000..7828d6bcb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unicode.rs
@@ -0,0 +1,36 @@
+// run-rustfix
+#[warn(clippy::invisible_characters)]
+fn zero() {
+ print!("Here >​< is a ZWS, and ​another");
+ print!("This\u{200B}is\u{200B}fine");
+ print!("Here >­< is a SHY, and ­another");
+ print!("This\u{ad}is\u{ad}fine");
+ print!("Here >⁠< is a WJ, and ⁠another");
+ print!("This\u{2060}is\u{2060}fine");
+}
+
+#[warn(clippy::unicode_not_nfc)]
+fn canon() {
+ print!("̀àh?");
+ print!("a\u{0300}h?"); // also ok
+}
+
+#[warn(clippy::non_ascii_literal)]
+fn uni() {
+ print!("Üben!");
+ print!("\u{DC}ben!"); // this is ok
+}
+
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+ const _EMPTY_BLOCK: char = '▱';
+ const _FULL_BLOCK: char = '▰';
+}
+
+fn main() {
+ zero();
+ uni();
+ canon();
+ single_quote();
+}
diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr
new file mode 100644
index 000000000..01d3f3c02
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unicode.stderr
@@ -0,0 +1,50 @@
+error: invisible character detected
+ --> $DIR/unicode.rs:4:12
+ |
+LL | print!("Here >​< is a ZWS, and ​another");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
+ |
+ = note: `-D clippy::invisible-characters` implied by `-D warnings`
+
+error: invisible character detected
+ --> $DIR/unicode.rs:6:12
+ |
+LL | print!("Here >­< is a SHY, and ­another");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
+
+error: invisible character detected
+ --> $DIR/unicode.rs:8:12
+ |
+LL | print!("Here >⁠< is a WJ, and ⁠another");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
+
+error: non-NFC Unicode sequence detected
+ --> $DIR/unicode.rs:14:12
+ |
+LL | print!("̀àh?");
+ | ^^^^^ help: consider replacing the string with: `"̀àh?"`
+ |
+ = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
+
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:20:12
+ |
+LL | print!("Üben!");
+ | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
+ |
+ = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
+
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:27:32
+ |
+LL | const _EMPTY_BLOCK: char = '▱';
+ | ^^^ help: consider replacing the string with: `'/u{25b1}'`
+
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:28:31
+ |
+LL | const _FULL_BLOCK: char = '▰';
+ | ^^^ help: consider replacing the string with: `'/u{25b0}'`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs
new file mode 100644
index 000000000..dac5ce272
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninit.rs
@@ -0,0 +1,26 @@
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::let_unit_value)]
+
+use std::mem::{self, MaybeUninit};
+
+fn main() {
+ let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // edge case: For now we lint on empty arrays
+ let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // edge case: For now we accept unit tuples
+ let _: () = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // This is OK, because `MaybeUninit` allows uninitialized data.
+ let _: MaybeUninit<usize> = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // This is OK, because all constitutent types are uninit-compatible.
+ let _: (MaybeUninit<usize>, MaybeUninit<bool>) = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // This is OK, because all constitutent types are uninit-compatible.
+ let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // Was a false negative.
+ let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+}
diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr
new file mode 100644
index 000000000..15ef23494
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninit.stderr
@@ -0,0 +1,22 @@
+error: this call for this type may be undefined behavior
+ --> $DIR/uninit.rs:7:29
+ |
+LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[deny(clippy::uninit_assumed_init)]` on by default
+
+error: this call for this type may be undefined behavior
+ --> $DIR/uninit.rs:10:31
+ |
+LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call for this type may be undefined behavior
+ --> $DIR/uninit.rs:25:29
+ |
+LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs
new file mode 100644
index 000000000..dc150cf28
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninit_vec.rs
@@ -0,0 +1,94 @@
+#![warn(clippy::uninit_vec)]
+
+use std::mem::MaybeUninit;
+
+#[derive(Default)]
+struct MyVec {
+ vec: Vec<u8>,
+}
+
+fn main() {
+ // with_capacity() -> set_len() should be detected
+ let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ unsafe {
+ vec.set_len(200);
+ }
+
+ // reserve() -> set_len() should be detected
+ vec.reserve(1000);
+ unsafe {
+ vec.set_len(200);
+ }
+
+ // new() -> set_len() should be detected
+ let mut vec: Vec<u8> = Vec::new();
+ unsafe {
+ vec.set_len(200);
+ }
+
+ // default() -> set_len() should be detected
+ let mut vec: Vec<u8> = Default::default();
+ unsafe {
+ vec.set_len(200);
+ }
+
+ let mut vec: Vec<u8> = Vec::default();
+ unsafe {
+ vec.set_len(200);
+ }
+
+ // test when both calls are enclosed in the same unsafe block
+ unsafe {
+ let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ vec.set_len(200);
+
+ vec.reserve(1000);
+ vec.set_len(200);
+ }
+
+ let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ unsafe {
+ // test the case where there are other statements in the following unsafe block
+ vec.set_len(200);
+ assert!(vec.len() == 200);
+ }
+
+ // handle vec stored in the field of a struct
+ let mut my_vec = MyVec::default();
+ my_vec.vec.reserve(1000);
+ unsafe {
+ my_vec.vec.set_len(200);
+ }
+
+ my_vec.vec = Vec::with_capacity(1000);
+ unsafe {
+ my_vec.vec.set_len(200);
+ }
+
+ // Test `#[allow(...)]` attributes on inner unsafe block (shouldn't trigger)
+ let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ #[allow(clippy::uninit_vec)]
+ unsafe {
+ vec.set_len(200);
+ }
+
+ // MaybeUninit-wrapped types should not be detected
+ unsafe {
+ let mut vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(1000);
+ vec.set_len(200);
+
+ let mut vec: Vec<(MaybeUninit<u8>, MaybeUninit<bool>)> = Vec::with_capacity(1000);
+ vec.set_len(200);
+
+ let mut vec: Vec<(MaybeUninit<u8>, [MaybeUninit<bool>; 2])> = Vec::with_capacity(1000);
+ vec.set_len(200);
+ }
+
+ // known false negative
+ let mut vec1: Vec<u8> = Vec::with_capacity(1000);
+ let mut vec2: Vec<u8> = Vec::with_capacity(1000);
+ unsafe {
+ vec1.set_len(200);
+ vec2.set_len(200);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr
new file mode 100644
index 000000000..520bfb26b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/uninit_vec.stderr
@@ -0,0 +1,105 @@
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:12:5
+ |
+LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::uninit-vec` implied by `-D warnings`
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:18:5
+ |
+LL | vec.reserve(1000);
+ | ^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` on empty `Vec` creates out-of-bound values
+ --> $DIR/uninit_vec.rs:24:5
+ |
+LL | let mut vec: Vec<u8> = Vec::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+
+error: calling `set_len()` on empty `Vec` creates out-of-bound values
+ --> $DIR/uninit_vec.rs:30:5
+ |
+LL | let mut vec: Vec<u8> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+
+error: calling `set_len()` on empty `Vec` creates out-of-bound values
+ --> $DIR/uninit_vec.rs:35:5
+ |
+LL | let mut vec: Vec<u8> = Vec::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:49:5
+ |
+LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:58:5
+ |
+LL | my_vec.vec.reserve(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | my_vec.vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:63:5
+ |
+LL | my_vec.vec = Vec::with_capacity(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | my_vec.vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:42:9
+ |
+LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:45:9
+ |
+LL | vec.reserve(1000);
+ | ^^^^^^^^^^^^^^^^^^
+LL | vec.set_len(200);
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs
new file mode 100644
index 000000000..38be87bdd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg.rs
@@ -0,0 +1,133 @@
+#![warn(clippy::unit_arg)]
+#![allow(
+ clippy::no_effect,
+ unused_must_use,
+ unused_variables,
+ clippy::unused_unit,
+ clippy::unnecessary_wraps,
+ clippy::or_fun_call,
+ clippy::needless_question_mark,
+ clippy::self_named_constructors,
+ clippy::let_unit_value
+)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+ println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+ println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+struct Bar;
+
+impl Bar {
+ fn bar<T: Debug>(&self, t: T) {
+ println!("{:?}", t);
+ }
+}
+
+fn baz<T: Debug>(t: T) {
+ foo(t);
+}
+
+trait Tr {
+ type Args;
+ fn do_it(args: Self::Args);
+}
+
+struct A;
+impl Tr for A {
+ type Args = ();
+ fn do_it(_: Self::Args) {}
+}
+
+struct B;
+impl Tr for B {
+ type Args = <A as Tr>::Args;
+
+ fn do_it(args: Self::Args) {
+ A::do_it(args)
+ }
+}
+
+fn bad() {
+ foo({
+ 1;
+ });
+ foo(foo(1));
+ foo({
+ foo(1);
+ foo(2);
+ });
+ let b = Bar;
+ b.bar({
+ 1;
+ });
+ taking_multiple_units(foo(0), foo(1));
+ taking_multiple_units(foo(0), {
+ foo(1);
+ foo(2);
+ });
+ taking_multiple_units(
+ {
+ foo(0);
+ foo(1);
+ },
+ {
+ foo(2);
+ foo(3);
+ },
+ );
+ // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block
+ None.or(Some(foo(2)));
+ // in this case, the suggestion can be inlined, no need for a surrounding block
+ // foo(()); foo(()) instead of { foo(()); foo(()) }
+ foo(foo(()));
+}
+
+fn ok() {
+ foo(());
+ foo(1);
+ foo({ 1 });
+ foo3("a", 3, vec![3]);
+ let b = Bar;
+ b.bar({ 1 });
+ b.bar(());
+ question_mark();
+ let named_unit_arg = ();
+ foo(named_unit_arg);
+ baz(());
+ B::do_it(());
+}
+
+fn question_mark() -> Result<(), ()> {
+ Ok(Ok(())?)?;
+ Ok(Ok(()))??;
+ Ok(())
+}
+
+#[allow(dead_code)]
+mod issue_2945 {
+ fn unit_fn() -> Result<(), i32> {
+ Ok(())
+ }
+
+ fn fallible() -> Result<(), i32> {
+ Ok(unit_fn()?)
+ }
+}
+
+#[allow(dead_code)]
+fn returning_expr() -> Option<()> {
+ Some(foo(1))
+}
+
+fn taking_multiple_units(a: (), b: ()) {}
+
+fn main() {
+ bad();
+ ok();
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr
new file mode 100644
index 000000000..11cfe66a3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg.stderr
@@ -0,0 +1,187 @@
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:57:5
+ |
+LL | / foo({
+LL | | 1;
+LL | | });
+ | |______^
+ |
+ = note: `-D clippy::unit-arg` implied by `-D warnings`
+help: remove the semicolon from the last statement in the block
+ |
+LL | 1
+ |
+help: or move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ {
+LL + 1;
+LL + };
+LL ~ foo(());
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:60:5
+ |
+LL | foo(foo(1));
+ | ^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ foo(1);
+LL ~ foo(());
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:61:5
+ |
+LL | / foo({
+LL | | foo(1);
+LL | | foo(2);
+LL | | });
+ | |______^
+ |
+help: remove the semicolon from the last statement in the block
+ |
+LL | foo(2)
+ |
+help: or move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ {
+LL + foo(1);
+LL + foo(2);
+LL + };
+LL ~ foo(());
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:66:5
+ |
+LL | / b.bar({
+LL | | 1;
+LL | | });
+ | |______^
+ |
+help: remove the semicolon from the last statement in the block
+ |
+LL | 1
+ |
+help: or move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ {
+LL + 1;
+LL + };
+LL ~ b.bar(());
+ |
+
+error: passing unit values to a function
+ --> $DIR/unit_arg.rs:69:5
+ |
+LL | taking_multiple_units(foo(0), foo(1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: move the expressions in front of the call and replace them with the unit literal `()`
+ |
+LL ~ foo(0);
+LL + foo(1);
+LL ~ taking_multiple_units((), ());
+ |
+
+error: passing unit values to a function
+ --> $DIR/unit_arg.rs:70:5
+ |
+LL | / taking_multiple_units(foo(0), {
+LL | | foo(1);
+LL | | foo(2);
+LL | | });
+ | |______^
+ |
+help: remove the semicolon from the last statement in the block
+ |
+LL | foo(2)
+ |
+help: or move the expressions in front of the call and replace them with the unit literal `()`
+ |
+LL ~ foo(0);
+LL + {
+LL + foo(1);
+LL + foo(2);
+LL + };
+LL ~ taking_multiple_units((), ());
+ |
+
+error: passing unit values to a function
+ --> $DIR/unit_arg.rs:74:5
+ |
+LL | / taking_multiple_units(
+LL | | {
+LL | | foo(0);
+LL | | foo(1);
+... |
+LL | | },
+LL | | );
+ | |_____^
+ |
+help: remove the semicolon from the last statement in the block
+ |
+LL | foo(1)
+ |
+help: remove the semicolon from the last statement in the block
+ |
+LL | foo(3)
+ |
+help: or move the expressions in front of the call and replace them with the unit literal `()`
+ |
+LL ~ {
+LL + foo(0);
+LL + foo(1);
+LL + };
+LL + {
+LL + foo(2);
+LL + foo(3);
+LL + };
+LL + taking_multiple_units(
+LL + (),
+LL + (),
+LL ~ );
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:85:13
+ |
+LL | None.or(Some(foo(2)));
+ | ^^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ None.or({
+LL + foo(2);
+LL + Some(())
+LL ~ });
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:88:5
+ |
+LL | foo(foo(()));
+ | ^^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ foo(());
+LL ~ foo(());
+ |
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:125:5
+ |
+LL | Some(foo(1))
+ | ^^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ foo(1);
+LL + Some(())
+ |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
new file mode 100644
index 000000000..9400e93ca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+#![warn(clippy::unit_arg)]
+#![allow(clippy::no_effect, unused_must_use, unused_variables)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+ println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+ println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+ foo(());
+ foo3((), 2, 2);
+ foo(0);
+ taking_two_units((), ());
+ foo(0);
+ foo(1);
+ taking_three_units((), (), ());
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+ bad();
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
new file mode 100644
index 000000000..5f52b6c53
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+#![warn(clippy::unit_arg)]
+#![allow(clippy::no_effect, unused_must_use, unused_variables)]
+
+use std::fmt::Debug;
+
+fn foo<T: Debug>(t: T) {
+ println!("{:?}", t);
+}
+
+fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
+ println!("{:?}, {:?}, {:?}", t1, t2, t3);
+}
+
+fn bad() {
+ foo({});
+ foo3({}, 2, 2);
+ taking_two_units({}, foo(0));
+ taking_three_units({}, foo(0), foo(1));
+}
+
+fn taking_two_units(a: (), b: ()) {}
+fn taking_three_units(a: (), b: (), c: ()) {}
+
+fn main() {
+ bad();
+}
diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
new file mode 100644
index 000000000..d35e93169
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
@@ -0,0 +1,45 @@
+error: passing a unit value to a function
+ --> $DIR/unit_arg_empty_blocks.rs:16:5
+ |
+LL | foo({});
+ | ^^^^--^
+ | |
+ | help: use a unit literal instead: `()`
+ |
+ = note: `-D clippy::unit-arg` implied by `-D warnings`
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg_empty_blocks.rs:17:5
+ |
+LL | foo3({}, 2, 2);
+ | ^^^^^--^^^^^^^
+ | |
+ | help: use a unit literal instead: `()`
+
+error: passing unit values to a function
+ --> $DIR/unit_arg_empty_blocks.rs:18:5
+ |
+LL | taking_two_units({}, foo(0));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL ~ foo(0);
+LL ~ taking_two_units((), ());
+ |
+
+error: passing unit values to a function
+ --> $DIR/unit_arg_empty_blocks.rs:19:5
+ |
+LL | taking_three_units({}, foo(0), foo(1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: move the expressions in front of the call and replace them with the unit literal `()`
+ |
+LL ~ foo(0);
+LL + foo(1);
+LL ~ taking_three_units((), (), ());
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs
new file mode 100644
index 000000000..3d2711043
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_cmp.rs
@@ -0,0 +1,61 @@
+#![warn(clippy::unit_cmp)]
+#![allow(
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::derive_partial_eq_without_eq
+)]
+
+#[derive(PartialEq)]
+pub struct ContainsUnit(()); // should be fine
+
+fn main() {
+ // this is fine
+ if true == false {}
+
+ // this warns
+ if {
+ true;
+ } == {
+ false;
+ } {}
+
+ if {
+ true;
+ } > {
+ false;
+ } {}
+
+ assert_eq!(
+ {
+ true;
+ },
+ {
+ false;
+ }
+ );
+ debug_assert_eq!(
+ {
+ true;
+ },
+ {
+ false;
+ }
+ );
+
+ assert_ne!(
+ {
+ true;
+ },
+ {
+ false;
+ }
+ );
+ debug_assert_ne!(
+ {
+ true;
+ },
+ {
+ false;
+ }
+ );
+}
diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr
new file mode 100644
index 000000000..41cf19ae6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_cmp.stderr
@@ -0,0 +1,74 @@
+error: ==-comparison of unit values detected. This will always be true
+ --> $DIR/unit_cmp.rs:16:8
+ |
+LL | if {
+ | ________^
+LL | | true;
+LL | | } == {
+LL | | false;
+LL | | } {}
+ | |_____^
+ |
+ = note: `-D clippy::unit-cmp` implied by `-D warnings`
+
+error: >-comparison of unit values detected. This will always be false
+ --> $DIR/unit_cmp.rs:22:8
+ |
+LL | if {
+ | ________^
+LL | | true;
+LL | | } > {
+LL | | false;
+LL | | } {}
+ | |_____^
+
+error: `assert_eq` of unit values detected. This will always succeed
+ --> $DIR/unit_cmp.rs:28:5
+ |
+LL | / assert_eq!(
+LL | | {
+LL | | true;
+LL | | },
+... |
+LL | | }
+LL | | );
+ | |_____^
+
+error: `debug_assert_eq` of unit values detected. This will always succeed
+ --> $DIR/unit_cmp.rs:36:5
+ |
+LL | / debug_assert_eq!(
+LL | | {
+LL | | true;
+LL | | },
+... |
+LL | | }
+LL | | );
+ | |_____^
+
+error: `assert_ne` of unit values detected. This will always fail
+ --> $DIR/unit_cmp.rs:45:5
+ |
+LL | / assert_ne!(
+LL | | {
+LL | | true;
+LL | | },
+... |
+LL | | }
+LL | | );
+ | |_____^
+
+error: `debug_assert_ne` of unit values detected. This will always fail
+ --> $DIR/unit_cmp.rs:53:5
+ |
+LL | / debug_assert_ne!(
+LL | | {
+LL | | true;
+LL | | },
+... |
+LL | | }
+LL | | );
+ | |_____^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unit_hash.rs b/src/tools/clippy/tests/ui/unit_hash.rs
new file mode 100644
index 000000000..43eb54eff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_hash.rs
@@ -0,0 +1,28 @@
+#![warn(clippy::unit_hash)]
+#![allow(clippy::let_unit_value)]
+
+use std::collections::hash_map::DefaultHasher;
+use std::hash::Hash;
+
+enum Foo {
+ Empty,
+ WithValue(u8),
+}
+
+fn do_nothing() {}
+
+fn main() {
+ let mut state = DefaultHasher::new();
+ let my_enum = Foo::Empty;
+
+ match my_enum {
+ Foo::Empty => ().hash(&mut state),
+ Foo::WithValue(x) => x.hash(&mut state),
+ }
+
+ let res = ();
+ res.hash(&mut state);
+
+ #[allow(clippy::unit_arg)]
+ do_nothing().hash(&mut state);
+}
diff --git a/src/tools/clippy/tests/ui/unit_hash.stderr b/src/tools/clippy/tests/ui/unit_hash.stderr
new file mode 100644
index 000000000..050fa55a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_hash.stderr
@@ -0,0 +1,27 @@
+error: this call to `hash` on the unit type will do nothing
+ --> $DIR/unit_hash.rs:19:23
+ |
+LL | Foo::Empty => ().hash(&mut state),
+ | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
+ |
+ = note: `-D clippy::unit-hash` implied by `-D warnings`
+ = note: the implementation of `Hash` for `()` is a no-op
+
+error: this call to `hash` on the unit type will do nothing
+ --> $DIR/unit_hash.rs:24:5
+ |
+LL | res.hash(&mut state);
+ | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
+ |
+ = note: the implementation of `Hash` for `()` is a no-op
+
+error: this call to `hash` on the unit type will do nothing
+ --> $DIR/unit_hash.rs:27:5
+ |
+LL | do_nothing().hash(&mut state);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
+ |
+ = note: the implementation of `Hash` for `()` is a no-op
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs
new file mode 100644
index 000000000..bdb4710cc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs
@@ -0,0 +1,36 @@
+#![warn(clippy::unit_return_expecting_ord)]
+#![allow(clippy::needless_return)]
+#![allow(clippy::unused_unit)]
+#![feature(is_sorted)]
+
+struct Struct {
+ field: isize,
+}
+
+fn double(i: isize) -> isize {
+ i * 2
+}
+
+fn unit(_i: isize) {}
+
+fn main() {
+ let mut structs = vec![Struct { field: 2 }, Struct { field: 1 }];
+ structs.sort_by_key(|s| {
+ double(s.field);
+ });
+ structs.sort_by_key(|s| double(s.field));
+ structs.is_sorted_by_key(|s| {
+ double(s.field);
+ });
+ structs.is_sorted_by_key(|s| {
+ if s.field > 0 {
+ ()
+ } else {
+ return ();
+ }
+ });
+ structs.sort_by_key(|s| {
+ return double(s.field);
+ });
+ structs.sort_by_key(|s| unit(s.field));
+}
diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr
new file mode 100644
index 000000000..e63d58746
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr
@@ -0,0 +1,39 @@
+error: this closure returns the unit type which also implements Ord
+ --> $DIR/unit_return_expecting_ord.rs:18:25
+ |
+LL | structs.sort_by_key(|s| {
+ | ^^^
+ |
+ = note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings`
+help: probably caused by this trailing semicolon
+ --> $DIR/unit_return_expecting_ord.rs:19:24
+ |
+LL | double(s.field);
+ | ^
+
+error: this closure returns the unit type which also implements PartialOrd
+ --> $DIR/unit_return_expecting_ord.rs:22:30
+ |
+LL | structs.is_sorted_by_key(|s| {
+ | ^^^
+ |
+help: probably caused by this trailing semicolon
+ --> $DIR/unit_return_expecting_ord.rs:23:24
+ |
+LL | double(s.field);
+ | ^
+
+error: this closure returns the unit type which also implements PartialOrd
+ --> $DIR/unit_return_expecting_ord.rs:25:30
+ |
+LL | structs.is_sorted_by_key(|s| {
+ | ^^^
+
+error: this closure returns the unit type which also implements Ord
+ --> $DIR/unit_return_expecting_ord.rs:35:25
+ |
+LL | structs.sort_by_key(|s| unit(s.field));
+ | ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unknown_attribute.rs b/src/tools/clippy/tests/ui/unknown_attribute.rs
new file mode 100644
index 000000000..e993e63f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unknown_attribute.rs
@@ -0,0 +1,3 @@
+#[clippy::unknown]
+#[clippy::cognitive_complexity = "1"]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unknown_attribute.stderr b/src/tools/clippy/tests/ui/unknown_attribute.stderr
new file mode 100644
index 000000000..618c5980d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unknown_attribute.stderr
@@ -0,0 +1,8 @@
+error: usage of unknown attribute
+ --> $DIR/unknown_attribute.rs:1:11
+ |
+LL | #[clippy::unknown]
+ | ^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
new file mode 100644
index 000000000..4249ff8a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::pedantic)]
+// Should suggest lowercase
+#![allow(clippy::all)]
+#![warn(clippy::cmp_nan)]
+
+// Should suggest similar clippy lint name
+#[warn(clippy::if_not_else)]
+#[warn(clippy::unnecessary_cast)]
+#[warn(clippy::useless_transmute)]
+// Shouldn't suggest rustc lint name(`dead_code`)
+#[warn(clippy::drop_copy)]
+// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
+#[warn(clippy::unused_self)]
+// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
+#[warn(clippy::redundant_static_lifetimes)]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
new file mode 100644
index 000000000..5db345f54
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::pedantic)]
+// Should suggest lowercase
+#![allow(clippy::All)]
+#![warn(clippy::CMP_NAN)]
+
+// Should suggest similar clippy lint name
+#[warn(clippy::if_not_els)]
+#[warn(clippy::UNNecsaRy_cAst)]
+#[warn(clippy::useles_transute)]
+// Shouldn't suggest rustc lint name(`dead_code`)
+#[warn(clippy::dead_cod)]
+// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
+#[warn(clippy::unused_colle)]
+// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
+#[warn(clippy::const_static_lifetim)]
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
new file mode 100644
index 000000000..421bf5ffa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
@@ -0,0 +1,52 @@
+error: unknown lint: `clippy::All`
+ --> $DIR/unknown_clippy_lints.rs:5:10
+ |
+LL | #![allow(clippy::All)]
+ | ^^^^^^^^^^^ help: did you mean: `clippy::all`
+ |
+ = note: `-D unknown-lints` implied by `-D warnings`
+
+error: unknown lint: `clippy::CMP_NAN`
+ --> $DIR/unknown_clippy_lints.rs:6:9
+ |
+LL | #![warn(clippy::CMP_NAN)]
+ | ^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_nan`
+
+error: unknown lint: `clippy::if_not_els`
+ --> $DIR/unknown_clippy_lints.rs:9:8
+ |
+LL | #[warn(clippy::if_not_els)]
+ | ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else`
+
+error: unknown lint: `clippy::UNNecsaRy_cAst`
+ --> $DIR/unknown_clippy_lints.rs:10:8
+ |
+LL | #[warn(clippy::UNNecsaRy_cAst)]
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_cast`
+
+error: unknown lint: `clippy::useles_transute`
+ --> $DIR/unknown_clippy_lints.rs:11:8
+ |
+LL | #[warn(clippy::useles_transute)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::useless_transmute`
+
+error: unknown lint: `clippy::dead_cod`
+ --> $DIR/unknown_clippy_lints.rs:13:8
+ |
+LL | #[warn(clippy::dead_cod)]
+ | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::drop_copy`
+
+error: unknown lint: `clippy::unused_colle`
+ --> $DIR/unknown_clippy_lints.rs:15:8
+ |
+LL | #[warn(clippy::unused_colle)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unused_self`
+
+error: unknown lint: `clippy::const_static_lifetim`
+ --> $DIR/unknown_clippy_lints.rs:17:8
+ |
+LL | #[warn(clippy::const_static_lifetim)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
new file mode 100644
index 000000000..b352b285c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::unnecessary_cast)]
+#![allow(
+ unused_must_use,
+ clippy::borrow_as_ptr,
+ clippy::no_effect,
+ clippy::nonstandard_macro_braces,
+ clippy::unnecessary_operation
+)]
+
+#[rustfmt::skip]
+fn main() {
+ // Test cast_unnecessary
+ 1_i32;
+ 1_f32;
+ false;
+ &1i32 as &i32;
+
+ -1_i32;
+ - 1_i32;
+ -1_f32;
+ 1_i32;
+ 1_f32;
+
+ // macro version
+ macro_rules! foo {
+ ($a:ident, $b:ident) => {
+ #[allow(unused)]
+ pub fn $a() -> $b {
+ 1 as $b
+ }
+ };
+ }
+ foo!(a, i32);
+ foo!(b, f32);
+ foo!(c, f64);
+
+ // do not lint cast to cfg-dependant type
+ 1 as std::os::raw::c_char;
+
+ // do not lint cast to alias type
+ 1 as I32Alias;
+ &1 as &I32Alias;
+}
+
+type I32Alias = i32;
+
+mod fixable {
+ #![allow(dead_code)]
+
+ fn main() {
+ // casting integer literal to float is unnecessary
+ 100_f32;
+ 100_f64;
+ 100_f64;
+ let _ = -100_f32;
+ let _ = -100_f64;
+ let _ = -100_f64;
+ 100_f32;
+ 100_f64;
+ // Should not trigger
+ #[rustfmt::skip]
+ let v = vec!(1);
+ &v as &[i32];
+ 0x10 as f32;
+ 0o10 as f32;
+ 0b10 as f32;
+ 0x11 as f64;
+ 0o11 as f64;
+ 0b11 as f64;
+
+ 1_u32;
+ 0x10_i32;
+ 0b10_usize;
+ 0o73_u16;
+ 1_000_000_000_u32;
+
+ 1.0_f64;
+ 0.5_f32;
+
+ 1.0 as u16;
+
+ let _ = -1_i32;
+ let _ = -1.0_f32;
+
+ let _ = 1 as I32Alias;
+ let _ = &1 as &I32Alias;
+ }
+
+ type I32Alias = i32;
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs
new file mode 100644
index 000000000..6c8cc3eff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::unnecessary_cast)]
+#![allow(
+ unused_must_use,
+ clippy::borrow_as_ptr,
+ clippy::no_effect,
+ clippy::nonstandard_macro_braces,
+ clippy::unnecessary_operation
+)]
+
+#[rustfmt::skip]
+fn main() {
+ // Test cast_unnecessary
+ 1i32 as i32;
+ 1f32 as f32;
+ false as bool;
+ &1i32 as &i32;
+
+ -1_i32 as i32;
+ - 1_i32 as i32;
+ -1f32 as f32;
+ 1_i32 as i32;
+ 1_f32 as f32;
+
+ // macro version
+ macro_rules! foo {
+ ($a:ident, $b:ident) => {
+ #[allow(unused)]
+ pub fn $a() -> $b {
+ 1 as $b
+ }
+ };
+ }
+ foo!(a, i32);
+ foo!(b, f32);
+ foo!(c, f64);
+
+ // do not lint cast to cfg-dependant type
+ 1 as std::os::raw::c_char;
+
+ // do not lint cast to alias type
+ 1 as I32Alias;
+ &1 as &I32Alias;
+}
+
+type I32Alias = i32;
+
+mod fixable {
+ #![allow(dead_code)]
+
+ fn main() {
+ // casting integer literal to float is unnecessary
+ 100 as f32;
+ 100 as f64;
+ 100_i32 as f64;
+ let _ = -100 as f32;
+ let _ = -100 as f64;
+ let _ = -100_i32 as f64;
+ 100. as f32;
+ 100. as f64;
+ // Should not trigger
+ #[rustfmt::skip]
+ let v = vec!(1);
+ &v as &[i32];
+ 0x10 as f32;
+ 0o10 as f32;
+ 0b10 as f32;
+ 0x11 as f64;
+ 0o11 as f64;
+ 0b11 as f64;
+
+ 1 as u32;
+ 0x10 as i32;
+ 0b10 as usize;
+ 0o73 as u16;
+ 1_000_000_000 as u32;
+
+ 1.0 as f64;
+ 0.5 as f32;
+
+ 1.0 as u16;
+
+ let _ = -1 as i32;
+ let _ = -1.0 as f32;
+
+ let _ = 1 as I32Alias;
+ let _ = &1 as &I32Alias;
+ }
+
+ type I32Alias = i32;
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
new file mode 100644
index 000000000..bad45f002
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr
@@ -0,0 +1,154 @@
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:14:5
+ |
+LL | 1i32 as i32;
+ | ^^^^^^^^^^^ help: try: `1_i32`
+ |
+ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:15:5
+ |
+LL | 1f32 as f32;
+ | ^^^^^^^^^^^ help: try: `1_f32`
+
+error: casting to the same type is unnecessary (`bool` -> `bool`)
+ --> $DIR/unnecessary_cast.rs:16:5
+ |
+LL | false as bool;
+ | ^^^^^^^^^^^^^ help: try: `false`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:19:5
+ |
+LL | -1_i32 as i32;
+ | ^^^^^^^^^^^^^ help: try: `-1_i32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:20:5
+ |
+LL | - 1_i32 as i32;
+ | ^^^^^^^^^^^^^^ help: try: `- 1_i32`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:21:5
+ |
+LL | -1f32 as f32;
+ | ^^^^^^^^^^^^ help: try: `-1_f32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:22:5
+ |
+LL | 1_i32 as i32;
+ | ^^^^^^^^^^^^ help: try: `1_i32`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:23:5
+ |
+LL | 1_f32 as f32;
+ | ^^^^^^^^^^^^ help: try: `1_f32`
+
+error: casting integer literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:53:9
+ |
+LL | 100 as f32;
+ | ^^^^^^^^^^ help: try: `100_f32`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:54:9
+ |
+LL | 100 as f64;
+ | ^^^^^^^^^^ help: try: `100_f64`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:55:9
+ |
+LL | 100_i32 as f64;
+ | ^^^^^^^^^^^^^^ help: try: `100_f64`
+
+error: casting integer literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:56:17
+ |
+LL | let _ = -100 as f32;
+ | ^^^^^^^^^^^ help: try: `-100_f32`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:57:17
+ |
+LL | let _ = -100 as f64;
+ | ^^^^^^^^^^^ help: try: `-100_f64`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:58:17
+ |
+LL | let _ = -100_i32 as f64;
+ | ^^^^^^^^^^^^^^^ help: try: `-100_f64`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:59:9
+ |
+LL | 100. as f32;
+ | ^^^^^^^^^^^ help: try: `100_f32`
+
+error: casting float literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:60:9
+ |
+LL | 100. as f64;
+ | ^^^^^^^^^^^ help: try: `100_f64`
+
+error: casting integer literal to `u32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:72:9
+ |
+LL | 1 as u32;
+ | ^^^^^^^^ help: try: `1_u32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:73:9
+ |
+LL | 0x10 as i32;
+ | ^^^^^^^^^^^ help: try: `0x10_i32`
+
+error: casting integer literal to `usize` is unnecessary
+ --> $DIR/unnecessary_cast.rs:74:9
+ |
+LL | 0b10 as usize;
+ | ^^^^^^^^^^^^^ help: try: `0b10_usize`
+
+error: casting integer literal to `u16` is unnecessary
+ --> $DIR/unnecessary_cast.rs:75:9
+ |
+LL | 0o73 as u16;
+ | ^^^^^^^^^^^ help: try: `0o73_u16`
+
+error: casting integer literal to `u32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:76:9
+ |
+LL | 1_000_000_000 as u32;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
+
+error: casting float literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast.rs:78:9
+ |
+LL | 1.0 as f64;
+ | ^^^^^^^^^^ help: try: `1.0_f64`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:79:9
+ |
+LL | 0.5 as f32;
+ | ^^^^^^^^^^ help: try: `0.5_f32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:83:17
+ |
+LL | let _ = -1 as i32;
+ | ^^^^^^^^^ help: try: `-1_i32`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast.rs:84:17
+ |
+LL | let _ = -1.0 as f32;
+ | ^^^^^^^^^^^ help: try: `-1.0_f32`
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs
new file mode 100644
index 000000000..6770a7fac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs
@@ -0,0 +1,110 @@
+// does not test any rustfixable lints
+
+#![warn(clippy::clone_on_ref_ptr)]
+#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)]
+
+use std::cell::RefCell;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+trait SomeTrait {}
+struct SomeImpl;
+impl SomeTrait for SomeImpl {}
+
+fn main() {}
+
+fn clone_on_ref_ptr() {
+ let rc = Rc::new(true);
+ let arc = Arc::new(true);
+
+ let rcweak = Rc::downgrade(&rc);
+ let arc_weak = Arc::downgrade(&arc);
+
+ rc.clone();
+ Rc::clone(&rc);
+
+ arc.clone();
+ Arc::clone(&arc);
+
+ rcweak.clone();
+ rc::Weak::clone(&rcweak);
+
+ arc_weak.clone();
+ sync::Weak::clone(&arc_weak);
+
+ let x = Arc::new(SomeImpl);
+ let _: Arc<dyn SomeTrait> = x.clone();
+}
+
+fn clone_on_copy_generic<T: Copy>(t: T) {
+ t.clone();
+
+ Some(t).clone();
+}
+
+fn clone_on_double_ref() {
+ let x = vec![1];
+ let y = &&x;
+ let z: &Vec<_> = y.clone();
+
+ println!("{:p} {:p}", *y, z);
+}
+
+mod many_derefs {
+ struct A;
+ struct B;
+ struct C;
+ struct D;
+ #[derive(Copy, Clone)]
+ struct E;
+
+ macro_rules! impl_deref {
+ ($src:ident, $dst:ident) => {
+ impl std::ops::Deref for $src {
+ type Target = $dst;
+ fn deref(&self) -> &Self::Target {
+ &$dst
+ }
+ }
+ };
+ }
+
+ impl_deref!(A, B);
+ impl_deref!(B, C);
+ impl_deref!(C, D);
+ impl std::ops::Deref for D {
+ type Target = &'static E;
+ fn deref(&self) -> &Self::Target {
+ &&E
+ }
+ }
+
+ fn go1() {
+ let a = A;
+ let _: E = a.clone();
+ let _: E = *****a;
+ }
+
+ fn check(mut encoded: &[u8]) {
+ let _ = &mut encoded.clone();
+ let _ = &encoded.clone();
+ }
+}
+
+mod issue2076 {
+ use std::rc::Rc;
+
+ macro_rules! try_opt {
+ ($expr: expr) => {
+ match $expr {
+ Some(value) => value,
+ None => return None,
+ }
+ };
+ }
+
+ fn func() -> Option<Rc<u8>> {
+ let rc = Rc::new(42);
+ Some(try_opt!(Some(rc)).clone())
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
new file mode 100644
index 000000000..94cc7777a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
@@ -0,0 +1,106 @@
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:23:5
+ |
+LL | rc.clone();
+ | ^^^^^^^^^^ help: try this: `Rc::<bool>::clone(&rc)`
+ |
+ = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
+
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:26:5
+ |
+LL | arc.clone();
+ | ^^^^^^^^^^^ help: try this: `Arc::<bool>::clone(&arc)`
+
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:29:5
+ |
+LL | rcweak.clone();
+ | ^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&rcweak)`
+
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:32:5
+ |
+LL | arc_weak.clone();
+ | ^^^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&arc_weak)`
+
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:36:33
+ |
+LL | let _: Arc<dyn SomeTrait> = x.clone();
+ | ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)`
+
+error: using `clone` on type `T` which implements the `Copy` trait
+ --> $DIR/unnecessary_clone.rs:40:5
+ |
+LL | t.clone();
+ | ^^^^^^^^^ help: try removing the `clone` call: `t`
+ |
+ = note: `-D clippy::clone-on-copy` implied by `-D warnings`
+
+error: using `clone` on type `std::option::Option<T>` which implements the `Copy` trait
+ --> $DIR/unnecessary_clone.rs:42:5
+ |
+LL | Some(t).clone();
+ | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
+
+error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec<i32>` instead of cloning the inner type
+ --> $DIR/unnecessary_clone.rs:48:22
+ |
+LL | let z: &Vec<_> = y.clone();
+ | ^^^^^^^^^
+ |
+ = note: `#[deny(clippy::clone_double_ref)]` on by default
+help: try dereferencing it
+ |
+LL | let z: &Vec<_> = &(*y).clone();
+ | ~~~~~~~~~~~~~
+help: or try being explicit if you are sure, that you want to clone a reference
+ |
+LL | let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: using `clone` on type `many_derefs::E` which implements the `Copy` trait
+ --> $DIR/unnecessary_clone.rs:84:20
+ |
+LL | let _: E = a.clone();
+ | ^^^^^^^^^ help: try dereferencing it: `*****a`
+
+error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type
+ --> $DIR/unnecessary_clone.rs:89:22
+ |
+LL | let _ = &mut encoded.clone();
+ | ^^^^^^^^^^^^^^^
+ |
+help: try dereferencing it
+ |
+LL | let _ = &mut &(*encoded).clone();
+ | ~~~~~~~~~~~~~~~~~~~
+help: or try being explicit if you are sure, that you want to clone a reference
+ |
+LL | let _ = &mut <&[u8]>::clone(encoded);
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type
+ --> $DIR/unnecessary_clone.rs:90:18
+ |
+LL | let _ = &encoded.clone();
+ | ^^^^^^^^^^^^^^^
+ |
+help: try dereferencing it
+ |
+LL | let _ = &&(*encoded).clone();
+ | ~~~~~~~~~~~~~~~~~~~
+help: or try being explicit if you are sure, that you want to clone a reference
+ |
+LL | let _ = &<&[u8]>::clone(encoded);
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: using `.clone()` on a ref-counted pointer
+ --> $DIR/unnecessary_clone.rs:108:14
+ |
+LL | Some(try_opt!(Some(rc)).clone())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::<u8>::clone(&try_opt!(Some(rc)))`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs
new file mode 100644
index 000000000..8e01c2674
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs
@@ -0,0 +1,150 @@
+#![allow(dead_code)]
+
+fn main() {
+ let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
+ let _ = (0..4).filter_map(|x| {
+ if x > 1 {
+ return Some(x);
+ };
+ None
+ });
+ let _ = (0..4).filter_map(|x| match x {
+ 0 | 1 => None,
+ _ => Some(x),
+ });
+
+ let _ = (0..4).filter_map(|x| Some(x + 1));
+
+ let _ = (0..4).filter_map(i32::checked_abs);
+}
+
+fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> {
+ "".chars().filter_map(|_| None)
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4433#issue-483920107
+mod comment_483920107 {
+ enum Severity {
+ Warning,
+ Other,
+ }
+
+ struct ServerError;
+
+ impl ServerError {
+ fn severity(&self) -> Severity {
+ Severity::Warning
+ }
+ }
+
+ struct S {
+ warnings: Vec<ServerError>,
+ }
+
+ impl S {
+ fn foo(&mut self, server_errors: Vec<ServerError>) {
+ #[allow(unused_variables)]
+ let errors: Vec<ServerError> = server_errors
+ .into_iter()
+ .filter_map(|se| match se.severity() {
+ Severity::Warning => {
+ self.warnings.push(se);
+ None
+ },
+ _ => Some(se),
+ })
+ .collect();
+ }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-611006622
+mod comment_611006622 {
+ struct PendingRequest {
+ reply_to: u8,
+ token: u8,
+ expires: u8,
+ group_id: u8,
+ }
+
+ enum Value {
+ Null,
+ }
+
+ struct Node;
+
+ impl Node {
+ fn send_response(&self, _reply_to: u8, _token: u8, _value: Value) -> &Self {
+ self
+ }
+ fn on_error_warn(&self) -> &Self {
+ self
+ }
+ }
+
+ struct S {
+ pending_requests: Vec<PendingRequest>,
+ }
+
+ impl S {
+ fn foo(&mut self, node: Node, now: u8, group_id: u8) {
+ // "drain_filter"
+ self.pending_requests = self
+ .pending_requests
+ .drain(..)
+ .filter_map(|pending| {
+ if pending.expires <= now {
+ return None; // Expired, remove
+ }
+
+ if pending.group_id == group_id {
+ // Matched - reuse strings and remove
+ node.send_response(pending.reply_to, pending.token, Value::Null)
+ .on_error_warn();
+ None
+ } else {
+ // Keep waiting
+ Some(pending)
+ }
+ })
+ .collect();
+ }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-621925270
+// This extrapolation doesn't reproduce the false positive. Additional context seems necessary.
+mod comment_621925270 {
+ struct Signature(u8);
+
+ fn foo(sig_packets: impl Iterator<Item = Result<Signature, ()>>) -> impl Iterator<Item = u8> {
+ sig_packets.filter_map(|res| match res {
+ Ok(Signature(sig_packet)) => Some(sig_packet),
+ _ => None,
+ })
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-1052978898
+mod comment_1052978898 {
+ #![allow(clippy::redundant_closure)]
+
+ pub struct S(u8);
+
+ impl S {
+ pub fn consume(self) {
+ println!("yum");
+ }
+ }
+
+ pub fn filter_owned() -> impl Iterator<Item = S> {
+ (0..10).map(|i| S(i)).filter_map(|s| {
+ if s.0 & 1 == 0 {
+ s.consume();
+ None
+ } else {
+ Some(s)
+ }
+ })
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
new file mode 100644
index 000000000..5585b10ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
@@ -0,0 +1,38 @@
+error: this `.filter_map` can be written more simply using `.filter`
+ --> $DIR/unnecessary_filter_map.rs:4:13
+ |
+LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
+
+error: this `.filter_map` can be written more simply using `.filter`
+ --> $DIR/unnecessary_filter_map.rs:5:13
+ |
+LL | let _ = (0..4).filter_map(|x| {
+ | _____________^
+LL | | if x > 1 {
+LL | | return Some(x);
+LL | | };
+LL | | None
+LL | | });
+ | |______^
+
+error: this `.filter_map` can be written more simply using `.filter`
+ --> $DIR/unnecessary_filter_map.rs:11:13
+ |
+LL | let _ = (0..4).filter_map(|x| match x {
+ | _____________^
+LL | | 0 | 1 => None,
+LL | | _ => Some(x),
+LL | | });
+ | |______^
+
+error: this `.filter_map` can be written more simply using `.map`
+ --> $DIR/unnecessary_filter_map.rs:16:13
+ |
+LL | let _ = (0..4).filter_map(|x| Some(x + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs
new file mode 100644
index 000000000..a52390861
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs
@@ -0,0 +1,23 @@
+#![allow(dead_code)]
+
+fn main() {
+ let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
+ let _ = (0..4).find_map(|x| {
+ if x > 1 {
+ return Some(x);
+ };
+ None
+ });
+ let _ = (0..4).find_map(|x| match x {
+ 0 | 1 => None,
+ _ => Some(x),
+ });
+
+ let _ = (0..4).find_map(|x| Some(x + 1));
+
+ let _ = (0..4).find_map(i32::checked_abs);
+}
+
+fn find_map_none_changes_item_type() -> Option<bool> {
+ "".chars().find_map(|_| None)
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr
new file mode 100644
index 000000000..fb33c122f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr
@@ -0,0 +1,38 @@
+error: this `.find_map` can be written more simply using `.find`
+ --> $DIR/unnecessary_find_map.rs:4:13
+ |
+LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-find-map` implied by `-D warnings`
+
+error: this `.find_map` can be written more simply using `.find`
+ --> $DIR/unnecessary_find_map.rs:5:13
+ |
+LL | let _ = (0..4).find_map(|x| {
+ | _____________^
+LL | | if x > 1 {
+LL | | return Some(x);
+LL | | };
+LL | | None
+LL | | });
+ | |______^
+
+error: this `.find_map` can be written more simply using `.find`
+ --> $DIR/unnecessary_find_map.rs:11:13
+ |
+LL | let _ = (0..4).find_map(|x| match x {
+ | _____________^
+LL | | 0 | 1 => None,
+LL | | _ => Some(x),
+LL | | });
+ | |______^
+
+error: this `.find_map` can be written more simply using `.map(..).next()`
+ --> $DIR/unnecessary_find_map.rs:16:13
+ |
+LL | let _ = (0..4).find_map(|x| Some(x + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
new file mode 100644
index 000000000..52300a3b6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+/// Calls which should trigger the `UNNECESSARY_FOLD` lint
+fn unnecessary_fold() {
+ // Can be replaced by .any
+ let _ = (0..3).any(|x| x > 2);
+ // Can be replaced by .all
+ let _ = (0..3).all(|x| x > 2);
+ // Can be replaced by .sum
+ let _: i32 = (0..3).sum();
+ // Can be replaced by .product
+ let _: i32 = (0..3).product();
+}
+
+/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
+fn unnecessary_fold_span_for_multi_element_chain() {
+ let _: bool = (0..3).map(|x| 2 * x).any(|x| x > 2);
+}
+
+/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
+fn unnecessary_fold_should_ignore() {
+ let _ = (0..3).fold(true, |acc, x| acc || x > 2);
+ let _ = (0..3).fold(false, |acc, x| acc && x > 2);
+ let _ = (0..3).fold(1, |acc, x| acc + x);
+ let _ = (0..3).fold(0, |acc, x| acc * x);
+ let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
+
+ // We only match against an accumulator on the left
+ // hand side. We could lint for .sum and .product when
+ // it's on the right, but don't for now (and this wouldn't
+ // be valid if we extended the lint to cover arbitrary numeric
+ // types).
+ let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
+ let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
+ let _ = (0..3).fold(0, |acc, x| x + acc);
+ let _ = (0..3).fold(1, |acc, x| x * acc);
+
+ let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
+ let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
+}
+
+/// Should lint only the line containing the fold
+fn unnecessary_fold_over_multiple_lines() {
+ let _ = (0..3)
+ .map(|x| x + 1)
+ .filter(|x| x % 2 == 0)
+ .any(|x| x > 2);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs
new file mode 100644
index 000000000..4028d80c0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs
@@ -0,0 +1,52 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+/// Calls which should trigger the `UNNECESSARY_FOLD` lint
+fn unnecessary_fold() {
+ // Can be replaced by .any
+ let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+ // Can be replaced by .all
+ let _ = (0..3).fold(true, |acc, x| acc && x > 2);
+ // Can be replaced by .sum
+ let _: i32 = (0..3).fold(0, |acc, x| acc + x);
+ // Can be replaced by .product
+ let _: i32 = (0..3).fold(1, |acc, x| acc * x);
+}
+
+/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
+fn unnecessary_fold_span_for_multi_element_chain() {
+ let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
+}
+
+/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
+fn unnecessary_fold_should_ignore() {
+ let _ = (0..3).fold(true, |acc, x| acc || x > 2);
+ let _ = (0..3).fold(false, |acc, x| acc && x > 2);
+ let _ = (0..3).fold(1, |acc, x| acc + x);
+ let _ = (0..3).fold(0, |acc, x| acc * x);
+ let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
+
+ // We only match against an accumulator on the left
+ // hand side. We could lint for .sum and .product when
+ // it's on the right, but don't for now (and this wouldn't
+ // be valid if we extended the lint to cover arbitrary numeric
+ // types).
+ let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
+ let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
+ let _ = (0..3).fold(0, |acc, x| x + acc);
+ let _ = (0..3).fold(1, |acc, x| x * acc);
+
+ let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
+ let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
+}
+
+/// Should lint only the line containing the fold
+fn unnecessary_fold_over_multiple_lines() {
+ let _ = (0..3)
+ .map(|x| x + 1)
+ .filter(|x| x % 2 == 0)
+ .fold(false, |acc, x| acc || x > 2);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
new file mode 100644
index 000000000..22c44588a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
@@ -0,0 +1,40 @@
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:8:20
+ |
+LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
+ |
+ = note: `-D clippy::unnecessary-fold` implied by `-D warnings`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:10:20
+ |
+LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:12:25
+ |
+LL | let _: i32 = (0..3).fold(0, |acc, x| acc + x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:14:25
+ |
+LL | let _: i32 = (0..3).fold(1, |acc, x| acc * x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:19:41
+ |
+LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
+
+error: this `.fold` can be written more succinctly using another method
+ --> $DIR/unnecessary_fold.rs:49:10
+ |
+LL | .fold(false, |acc, x| acc || x > 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
new file mode 100644
index 000000000..e01e9f07b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
@@ -0,0 +1,142 @@
+// run-rustfix
+
+#![allow(unused_assignments)]
+#![warn(clippy::unnecessary_to_owned)]
+
+#[allow(dead_code)]
+#[derive(Clone, Copy)]
+enum FileType {
+ Account,
+ PrivateKey,
+ Certificate,
+}
+
+fn main() {
+ let path = std::path::Path::new("x");
+
+ let _ = check_files(&[(FileType::Account, path)]);
+ let _ = check_files_vec(vec![(FileType::Account, path)]);
+
+ // negative tests
+ let _ = check_files_ref(&[(FileType::Account, path)]);
+ let _ = check_files_mut(&[(FileType::Account, path)]);
+ let _ = check_files_ref_mut(&[(FileType::Account, path)]);
+ let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
+ let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
+}
+
+// `check_files` and its variants are based on:
+// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
+fn check_files(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (t, path) in files {
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool {
+ for (t, path) in files.iter() {
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (ref t, path) in files.iter().copied() {
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+#[allow(unused_assignments)]
+fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (mut t, path) in files.iter().copied() {
+ t = FileType::PrivateKey;
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (ref mut t, path) in files.iter().copied() {
+ *t = FileType::PrivateKey;
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (t, path) in files.iter().copied() {
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.join(path).is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+#[allow(unused_assignments)]
+fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
+ for (mut t, path) in files.iter().cloned() {
+ t = FileType::PrivateKey;
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
+ Ok(std::path::PathBuf::new())
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
new file mode 100644
index 000000000..6ef2966c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
@@ -0,0 +1,142 @@
+// run-rustfix
+
+#![allow(unused_assignments)]
+#![warn(clippy::unnecessary_to_owned)]
+
+#[allow(dead_code)]
+#[derive(Clone, Copy)]
+enum FileType {
+ Account,
+ PrivateKey,
+ Certificate,
+}
+
+fn main() {
+ let path = std::path::Path::new("x");
+
+ let _ = check_files(&[(FileType::Account, path)]);
+ let _ = check_files_vec(vec![(FileType::Account, path)]);
+
+ // negative tests
+ let _ = check_files_ref(&[(FileType::Account, path)]);
+ let _ = check_files_mut(&[(FileType::Account, path)]);
+ let _ = check_files_ref_mut(&[(FileType::Account, path)]);
+ let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
+ let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
+}
+
+// `check_files` and its variants are based on:
+// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
+fn check_files(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (t, path) in files.iter().copied() {
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool {
+ for (t, path) in files.iter().copied() {
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (ref t, path) in files.iter().copied() {
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+#[allow(unused_assignments)]
+fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (mut t, path) in files.iter().copied() {
+ t = FileType::PrivateKey;
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (ref mut t, path) in files.iter().copied() {
+ *t = FileType::PrivateKey;
+ let other = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool {
+ for (t, path) in files.iter().copied() {
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.join(path).is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+#[allow(unused_assignments)]
+fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
+ for (mut t, path) in files.iter().cloned() {
+ t = FileType::PrivateKey;
+ let other = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() || !other.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
+ Ok(std::path::PathBuf::new())
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
new file mode 100644
index 000000000..8f151e620
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
@@ -0,0 +1,35 @@
+error: unnecessary use of `copied`
+ --> $DIR/unnecessary_iter_cloned.rs:31:22
+ |
+LL | for (t, path) in files.iter().copied() {
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
+help: use
+ |
+LL | for (t, path) in files {
+ | ~~~~~
+help: remove this `&`
+ |
+LL - let other = match get_file_path(&t) {
+LL + let other = match get_file_path(t) {
+ |
+
+error: unnecessary use of `copied`
+ --> $DIR/unnecessary_iter_cloned.rs:46:22
+ |
+LL | for (t, path) in files.iter().copied() {
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use
+ |
+LL | for (t, path) in files.iter() {
+ | ~~~~~~~~~~~~
+help: remove this `&`
+ |
+LL - let other = match get_file_path(&t) {
+LL + let other = match get_file_path(t) {
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.fixed b/src/tools/clippy/tests/ui/unnecessary_join.fixed
new file mode 100644
index 000000000..7e12c6ae4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_join)]
+
+fn main() {
+ // should be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<String>();
+ println!("{}", output);
+
+ // should be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<String>();
+ println!("{}", output);
+
+ // should not be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<Vec<String>>()
+ .join("\n");
+ println!("{}", output);
+
+ // should not be linted
+ let vector = vec!["hello", "world"];
+ let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+ println!("{}", output);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.rs b/src/tools/clippy/tests/ui/unnecessary_join.rs
new file mode 100644
index 000000000..0a21656a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_join)]
+
+fn main() {
+ // should be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<Vec<String>>()
+ .join("");
+ println!("{}", output);
+
+ // should be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<Vec<_>>()
+ .join("");
+ println!("{}", output);
+
+ // should not be linted
+ let vector = vec!["hello", "world"];
+ let output = vector
+ .iter()
+ .map(|item| item.to_uppercase())
+ .collect::<Vec<String>>()
+ .join("\n");
+ println!("{}", output);
+
+ // should not be linted
+ let vector = vec!["hello", "world"];
+ let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+ println!("{}", output);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_join.stderr b/src/tools/clippy/tests/ui/unnecessary_join.stderr
new file mode 100644
index 000000000..0b14b143a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_join.stderr
@@ -0,0 +1,20 @@
+error: called `.collect<Vec<String>>().join("")` on an iterator
+ --> $DIR/unnecessary_join.rs:11:10
+ |
+LL | .collect::<Vec<String>>()
+ | __________^
+LL | | .join("");
+ | |_________________^ help: try using: `collect::<String>()`
+ |
+ = note: `-D clippy::unnecessary-join` implied by `-D warnings`
+
+error: called `.collect<Vec<String>>().join("")` on an iterator
+ --> $DIR/unnecessary_join.rs:20:10
+ |
+LL | .collect::<Vec<_>>()
+ | __________^
+LL | | .join("");
+ | |_________________^ help: try using: `collect::<String>()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
new file mode 100644
index 000000000..eed817968
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -0,0 +1,132 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::map_identity)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+ some_field: usize,
+}
+
+impl SomeStruct {
+ fn return_some_field(&self) -> usize {
+ self.some_field
+ }
+}
+
+fn some_call<T: Default>() -> T {
+ T::default()
+}
+
+fn main() {
+ let astronomers_pi = 10;
+ let ext_arr: [usize; 1] = [2];
+ let ext_str = SomeStruct { some_field: 10 };
+
+ let mut opt = Some(42);
+ let ext_opt = Some(42);
+ let nested_opt = Some(Some(42));
+ let nested_tuple_opt = Some(Some((42, 43)));
+ let cond = true;
+
+ // Should lint - Option
+ let _ = opt.unwrap_or(2);
+ let _ = opt.unwrap_or(astronomers_pi);
+ let _ = opt.unwrap_or(ext_str.some_field);
+ let _ = opt.unwrap_or_else(|| ext_arr[0]);
+ let _ = opt.and(ext_opt);
+ let _ = opt.or(ext_opt);
+ let _ = opt.or(None);
+ let _ = opt.get_or_insert(2);
+ let _ = opt.ok_or(2);
+ let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+ let _ = cond.then_some(astronomers_pi);
+
+ // Cases when unwrap is not called on a simple variable
+ let _ = Some(10).unwrap_or(2);
+ let _ = Some(10).and(ext_opt);
+ let _: Option<usize> = None.or(ext_opt);
+ let _ = None.get_or_insert(2);
+ let _: Result<usize, usize> = None.ok_or(2);
+ let _: Option<usize> = None.or(None);
+
+ let mut deep = Deep(Some(42));
+ let _ = deep.0.unwrap_or(2);
+ let _ = deep.0.and(ext_opt);
+ let _ = deep.0.or(None);
+ let _ = deep.0.get_or_insert(2);
+ let _ = deep.0.ok_or(2);
+
+ // Should not lint - Option
+ let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+ let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+ let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+ let _ = opt.or_else(some_call);
+ let _ = opt.or_else(|| some_call());
+ let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+ let _: Result<usize, usize> = opt.ok_or_else(some_call);
+ let _ = deep.0.get_or_insert_with(|| some_call());
+ let _ = deep.0.or_else(some_call);
+ let _ = deep.0.or_else(|| some_call());
+ let _ = opt.ok_or_else(|| ext_arr[0]);
+
+ // should not lint, bind_instead_of_map takes priority
+ let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+ let _ = Some(10).and_then(|idx| Some(idx));
+
+ // should lint, bind_instead_of_map doesn't apply
+ let _: Option<usize> = None.or(Some(3));
+ let _ = deep.0.or(Some(3));
+ let _ = opt.or(Some(3));
+
+ // Should lint - Result
+ let res: Result<usize, usize> = Err(5);
+ let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+ let _ = res2.unwrap_or(2);
+ let _ = res2.unwrap_or(astronomers_pi);
+ let _ = res2.unwrap_or(ext_str.some_field);
+
+ // Should not lint - Result
+ let _ = res.unwrap_or_else(|err| err);
+ let _ = res.unwrap_or_else(|err| ext_arr[err]);
+ let _ = res2.unwrap_or_else(|err| err.some_field);
+ let _ = res2.unwrap_or_else(|err| err.return_some_field());
+ let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+ // should not lint, bind_instead_of_map takes priority
+ let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+ let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+ let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+ let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+ let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+ let _: Result<usize, usize> = res.or_else(|_| Err(2));
+ let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+ let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+
+ // should lint, bind_instead_of_map doesn't apply
+ let _: Result<usize, usize> = res.and(Err(2));
+ let _: Result<usize, usize> = res.and(Err(astronomers_pi));
+ let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
+
+ let _: Result<usize, usize> = res.or(Ok(2));
+ let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
+ let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+ let _: Result<usize, usize> = res.
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ or(Ok(ext_str.some_field));
+
+ // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
+ let _: Result<usize, usize> = res.and_then(|x| Err(x));
+ let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
new file mode 100644
index 000000000..1588db79b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -0,0 +1,132 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::map_identity)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+ some_field: usize,
+}
+
+impl SomeStruct {
+ fn return_some_field(&self) -> usize {
+ self.some_field
+ }
+}
+
+fn some_call<T: Default>() -> T {
+ T::default()
+}
+
+fn main() {
+ let astronomers_pi = 10;
+ let ext_arr: [usize; 1] = [2];
+ let ext_str = SomeStruct { some_field: 10 };
+
+ let mut opt = Some(42);
+ let ext_opt = Some(42);
+ let nested_opt = Some(Some(42));
+ let nested_tuple_opt = Some(Some((42, 43)));
+ let cond = true;
+
+ // Should lint - Option
+ let _ = opt.unwrap_or_else(|| 2);
+ let _ = opt.unwrap_or_else(|| astronomers_pi);
+ let _ = opt.unwrap_or_else(|| ext_str.some_field);
+ let _ = opt.unwrap_or_else(|| ext_arr[0]);
+ let _ = opt.and_then(|_| ext_opt);
+ let _ = opt.or_else(|| ext_opt);
+ let _ = opt.or_else(|| None);
+ let _ = opt.get_or_insert_with(|| 2);
+ let _ = opt.ok_or_else(|| 2);
+ let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+ let _ = cond.then(|| astronomers_pi);
+
+ // Cases when unwrap is not called on a simple variable
+ let _ = Some(10).unwrap_or_else(|| 2);
+ let _ = Some(10).and_then(|_| ext_opt);
+ let _: Option<usize> = None.or_else(|| ext_opt);
+ let _ = None.get_or_insert_with(|| 2);
+ let _: Result<usize, usize> = None.ok_or_else(|| 2);
+ let _: Option<usize> = None.or_else(|| None);
+
+ let mut deep = Deep(Some(42));
+ let _ = deep.0.unwrap_or_else(|| 2);
+ let _ = deep.0.and_then(|_| ext_opt);
+ let _ = deep.0.or_else(|| None);
+ let _ = deep.0.get_or_insert_with(|| 2);
+ let _ = deep.0.ok_or_else(|| 2);
+
+ // Should not lint - Option
+ let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+ let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+ let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+ let _ = opt.or_else(some_call);
+ let _ = opt.or_else(|| some_call());
+ let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+ let _: Result<usize, usize> = opt.ok_or_else(some_call);
+ let _ = deep.0.get_or_insert_with(|| some_call());
+ let _ = deep.0.or_else(some_call);
+ let _ = deep.0.or_else(|| some_call());
+ let _ = opt.ok_or_else(|| ext_arr[0]);
+
+ // should not lint, bind_instead_of_map takes priority
+ let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+ let _ = Some(10).and_then(|idx| Some(idx));
+
+ // should lint, bind_instead_of_map doesn't apply
+ let _: Option<usize> = None.or_else(|| Some(3));
+ let _ = deep.0.or_else(|| Some(3));
+ let _ = opt.or_else(|| Some(3));
+
+ // Should lint - Result
+ let res: Result<usize, usize> = Err(5);
+ let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+ let _ = res2.unwrap_or_else(|_| 2);
+ let _ = res2.unwrap_or_else(|_| astronomers_pi);
+ let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+
+ // Should not lint - Result
+ let _ = res.unwrap_or_else(|err| err);
+ let _ = res.unwrap_or_else(|err| ext_arr[err]);
+ let _ = res2.unwrap_or_else(|err| err.some_field);
+ let _ = res2.unwrap_or_else(|err| err.return_some_field());
+ let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+ // should not lint, bind_instead_of_map takes priority
+ let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+ let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+ let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+ let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+ let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+ let _: Result<usize, usize> = res.or_else(|_| Err(2));
+ let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+ let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+
+ // should lint, bind_instead_of_map doesn't apply
+ let _: Result<usize, usize> = res.and_then(|_| Err(2));
+ let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+ let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+
+ let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+ let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+ let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+ let _: Result<usize, usize> = res.
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ or_else(|_| Ok(ext_str.some_field));
+
+ // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
+ let _: Result<usize, usize> = res.and_then(|x| Err(x));
+ let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
new file mode 100644
index 000000000..83dc7fd83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -0,0 +1,283 @@
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:36:13
+ |
+LL | let _ = opt.unwrap_or_else(|| 2);
+ | ^^^^--------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+ |
+ = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:37:13
+ |
+LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
+ | ^^^^---------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:38:13
+ |
+LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
+ | ^^^^-------------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:40:13
+ |
+LL | let _ = opt.and_then(|_| ext_opt);
+ | ^^^^---------------------
+ | |
+ | help: use `and(..)` instead: `and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:41:13
+ |
+LL | let _ = opt.or_else(|| ext_opt);
+ | ^^^^-------------------
+ | |
+ | help: use `or(..)` instead: `or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:42:13
+ |
+LL | let _ = opt.or_else(|| None);
+ | ^^^^----------------
+ | |
+ | help: use `or(..)` instead: `or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:43:13
+ |
+LL | let _ = opt.get_or_insert_with(|| 2);
+ | ^^^^------------------------
+ | |
+ | help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:44:13
+ |
+LL | let _ = opt.ok_or_else(|| 2);
+ | ^^^^----------------
+ | |
+ | help: use `ok_or(..)` instead: `ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:45:13
+ |
+LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+ | ^^^^^^^^^^^^^^^^^-------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
+
+error: unnecessary closure used with `bool::then`
+ --> $DIR/unnecessary_lazy_eval.rs:46:13
+ |
+LL | let _ = cond.then(|| astronomers_pi);
+ | ^^^^^-----------------------
+ | |
+ | help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:49:13
+ |
+LL | let _ = Some(10).unwrap_or_else(|| 2);
+ | ^^^^^^^^^--------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:50:13
+ |
+LL | let _ = Some(10).and_then(|_| ext_opt);
+ | ^^^^^^^^^---------------------
+ | |
+ | help: use `and(..)` instead: `and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:51:28
+ |
+LL | let _: Option<usize> = None.or_else(|| ext_opt);
+ | ^^^^^-------------------
+ | |
+ | help: use `or(..)` instead: `or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:52:13
+ |
+LL | let _ = None.get_or_insert_with(|| 2);
+ | ^^^^^------------------------
+ | |
+ | help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:53:35
+ |
+LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
+ | ^^^^^----------------
+ | |
+ | help: use `ok_or(..)` instead: `ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:54:28
+ |
+LL | let _: Option<usize> = None.or_else(|| None);
+ | ^^^^^----------------
+ | |
+ | help: use `or(..)` instead: `or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:57:13
+ |
+LL | let _ = deep.0.unwrap_or_else(|| 2);
+ | ^^^^^^^--------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:58:13
+ |
+LL | let _ = deep.0.and_then(|_| ext_opt);
+ | ^^^^^^^---------------------
+ | |
+ | help: use `and(..)` instead: `and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:59:13
+ |
+LL | let _ = deep.0.or_else(|| None);
+ | ^^^^^^^----------------
+ | |
+ | help: use `or(..)` instead: `or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:60:13
+ |
+LL | let _ = deep.0.get_or_insert_with(|| 2);
+ | ^^^^^^^------------------------
+ | |
+ | help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:61:13
+ |
+LL | let _ = deep.0.ok_or_else(|| 2);
+ | ^^^^^^^----------------
+ | |
+ | help: use `ok_or(..)` instead: `ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:81:28
+ |
+LL | let _: Option<usize> = None.or_else(|| Some(3));
+ | ^^^^^-------------------
+ | |
+ | help: use `or(..)` instead: `or(Some(3))`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:82:13
+ |
+LL | let _ = deep.0.or_else(|| Some(3));
+ | ^^^^^^^-------------------
+ | |
+ | help: use `or(..)` instead: `or(Some(3))`
+
+error: unnecessary closure used to substitute value for `Option::None`
+ --> $DIR/unnecessary_lazy_eval.rs:83:13
+ |
+LL | let _ = opt.or_else(|| Some(3));
+ | ^^^^-------------------
+ | |
+ | help: use `or(..)` instead: `or(Some(3))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:89:13
+ |
+LL | let _ = res2.unwrap_or_else(|_| 2);
+ | ^^^^^---------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:90:13
+ |
+LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
+ | ^^^^^----------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:91:13
+ |
+LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+ | ^^^^^--------------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:113:35
+ |
+LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
+ | ^^^^--------------------
+ | |
+ | help: use `and(..)` instead: `and(Err(2))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:114:35
+ |
+LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+ | ^^^^---------------------------------
+ | |
+ | help: use `and(..)` instead: `and(Err(astronomers_pi))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:115:35
+ |
+LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+ | ^^^^-------------------------------------
+ | |
+ | help: use `and(..)` instead: `and(Err(ext_str.some_field))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:117:35
+ |
+LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+ | ^^^^------------------
+ | |
+ | help: use `or(..)` instead: `or(Ok(2))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:118:35
+ |
+LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+ | ^^^^-------------------------------
+ | |
+ | help: use `or(..)` instead: `or(Ok(astronomers_pi))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:119:35
+ |
+LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+ | ^^^^-----------------------------------
+ | |
+ | help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval.rs:120:35
+ |
+LL | let _: Result<usize, usize> = res.
+ | ___________________________________^
+LL | | // some lines
+LL | | // some lines
+LL | | // some lines
+... |
+LL | | // some lines
+LL | | or_else(|_| Ok(ext_str.some_field));
+ | |_________----------------------------------^
+ | |
+ | help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+
+error: aborting due to 34 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
new file mode 100644
index 000000000..b05dd143b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::unnecessary_lazy_evaluations)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+ some_field: usize,
+}
+
+fn main() {
+ // fix will break type inference
+ let _ = Ok(1).unwrap_or_else(|()| 2);
+ mod e {
+ pub struct E;
+ }
+ let _ = Ok(1).unwrap_or_else(|e::E| 2);
+ let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+
+ // Fix #6343
+ let arr = [(Some(1),)];
+ Some(&0).and_then(|&i| arr[i].0);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
new file mode 100644
index 000000000..20acab6e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -0,0 +1,28 @@
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13
+ |
+LL | let _ = Ok(1).unwrap_or_else(|()| 2);
+ | ^^^^^^----------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+ |
+ = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13
+ |
+LL | let _ = Ok(1).unwrap_or_else(|e::E| 2);
+ | ^^^^^^------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+ --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
+ |
+LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+ | ^^^^^^-------------------------------------
+ | |
+ | help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
new file mode 100644
index 000000000..bf0ec8deb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -0,0 +1,79 @@
+// run-rustfix
+
+#![feature(box_syntax)]
+#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
+#![warn(clippy::unnecessary_operation)]
+
+struct Tuple(i32);
+struct Struct {
+ field: i32,
+}
+enum Enum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+struct DropStruct {
+ field: i32,
+}
+impl Drop for DropStruct {
+ fn drop(&mut self) {}
+}
+struct DropTuple(i32);
+impl Drop for DropTuple {
+ fn drop(&mut self) {}
+}
+enum DropEnum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+impl Drop for DropEnum {
+ fn drop(&mut self) {}
+}
+struct FooString {
+ s: String,
+}
+
+fn get_number() -> i32 {
+ 0
+}
+
+fn get_usize() -> usize {
+ 0
+}
+fn get_struct() -> Struct {
+ Struct { field: 0 }
+}
+fn get_drop_struct() -> DropStruct {
+ DropStruct { field: 0 }
+}
+
+fn main() {
+ get_number();
+ get_number();
+ get_struct();
+ get_number();
+ get_number();
+ 5;get_number();
+ get_number();
+ get_number();
+ 5;6;get_number();
+ get_number();
+ get_number();
+ get_number();
+ 5;get_number();
+ 42;get_number();
+ assert!([42, 55].len() > get_usize());
+ 42;get_number();
+ get_number();
+ assert!([42; 55].len() > get_usize());
+ get_number();
+ String::from("blah");
+
+ // Do not warn
+ DropTuple(get_number());
+ DropStruct { field: get_number() };
+ DropStruct { field: get_number() };
+ DropStruct { ..get_drop_struct() };
+ DropEnum::Tuple(get_number());
+ DropEnum::Struct { field: get_number() };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
new file mode 100644
index 000000000..08cb9ab52
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -0,0 +1,83 @@
+// run-rustfix
+
+#![feature(box_syntax)]
+#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
+#![warn(clippy::unnecessary_operation)]
+
+struct Tuple(i32);
+struct Struct {
+ field: i32,
+}
+enum Enum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+struct DropStruct {
+ field: i32,
+}
+impl Drop for DropStruct {
+ fn drop(&mut self) {}
+}
+struct DropTuple(i32);
+impl Drop for DropTuple {
+ fn drop(&mut self) {}
+}
+enum DropEnum {
+ Tuple(i32),
+ Struct { field: i32 },
+}
+impl Drop for DropEnum {
+ fn drop(&mut self) {}
+}
+struct FooString {
+ s: String,
+}
+
+fn get_number() -> i32 {
+ 0
+}
+
+fn get_usize() -> usize {
+ 0
+}
+fn get_struct() -> Struct {
+ Struct { field: 0 }
+}
+fn get_drop_struct() -> DropStruct {
+ DropStruct { field: 0 }
+}
+
+fn main() {
+ Tuple(get_number());
+ Struct { field: get_number() };
+ Struct { ..get_struct() };
+ Enum::Tuple(get_number());
+ Enum::Struct { field: get_number() };
+ 5 + get_number();
+ *&get_number();
+ &get_number();
+ (5, 6, get_number());
+ box get_number();
+ get_number()..;
+ ..get_number();
+ 5..get_number();
+ [42, get_number()];
+ [42, 55][get_usize()];
+ (42, get_number()).1;
+ [get_number(); 55];
+ [42; 55][get_usize()];
+ {
+ get_number()
+ };
+ FooString {
+ s: String::from("blah"),
+ };
+
+ // Do not warn
+ DropTuple(get_number());
+ DropStruct { field: get_number() };
+ DropStruct { field: get_number() };
+ DropStruct { ..get_drop_struct() };
+ DropEnum::Tuple(get_number());
+ DropEnum::Struct { field: get_number() };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
new file mode 100644
index 000000000..f66d08ecb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -0,0 +1,128 @@
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:51:5
+ |
+LL | Tuple(get_number());
+ | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+ |
+ = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:52:5
+ |
+LL | Struct { field: get_number() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:53:5
+ |
+LL | Struct { ..get_struct() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:54:5
+ |
+LL | Enum::Tuple(get_number());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:55:5
+ |
+LL | Enum::Struct { field: get_number() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:56:5
+ |
+LL | 5 + get_number();
+ | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:57:5
+ |
+LL | *&get_number();
+ | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:58:5
+ |
+LL | &get_number();
+ | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:59:5
+ |
+LL | (5, 6, get_number());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:60:5
+ |
+LL | box get_number();
+ | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:61:5
+ |
+LL | get_number()..;
+ | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:62:5
+ |
+LL | ..get_number();
+ | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:63:5
+ |
+LL | 5..get_number();
+ | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:64:5
+ |
+LL | [42, get_number()];
+ | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:65:5
+ |
+LL | [42, 55][get_usize()];
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:66:5
+ |
+LL | (42, get_number()).1;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:67:5
+ |
+LL | [get_number(); 55];
+ | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:68:5
+ |
+LL | [42; 55][get_usize()];
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:69:5
+ |
+LL | / {
+LL | | get_number()
+LL | | };
+ | |______^ help: statement can be reduced to: `get_number();`
+
+error: unnecessary operation
+ --> $DIR/unnecessary_operation.rs:72:5
+ |
+LL | / FooString {
+LL | | s: String::from("blah"),
+LL | | };
+ | |______^ help: statement can be reduced to: `String::from("blah");`
+
+error: aborting due to 20 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed
new file mode 100644
index 000000000..f95f91329
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument("");
+
+ // should be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs
new file mode 100644
index 000000000..0cbdc151e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument(&String::new());
+
+ // should be linted
+ ref_str_argument(&String::from(""));
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr
new file mode 100644
index 000000000..46bc4597b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr
@@ -0,0 +1,16 @@
+error: usage of `&String::new()` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_strings.rs:12:22
+ |
+LL | ref_str_argument(&String::new());
+ | ^^^^^^^^^^^^^^ help: try: `""`
+ |
+ = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
+
+error: usage of `&String::from("")` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_strings.rs:15:22
+ |
+LL | ref_str_argument(&String::from(""));
+ | ^^^^^^^^^^^^^^^^^ help: try: `""`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed b/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed
new file mode 100644
index 000000000..1185eaa1d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs as alias;
+use std::io::{self, Read};
+use std::rc;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.rs b/src/tools/clippy/tests/ui/unnecessary_self_imports.rs
new file mode 100644
index 000000000..56bfbc094
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs::{self as alias};
+use std::io::{self, Read};
+use std::rc::{self};
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr b/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr
new file mode 100644
index 000000000..83a5618c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr
@@ -0,0 +1,23 @@
+error: import ending with `::{self}`
+ --> $DIR/unnecessary_self_imports.rs:6:1
+ |
+LL | use std::fs::{self as alias};
+ | ^^^^^^^^^--------------------
+ | |
+ | help: consider omitting `::{self}`: `fs as alias;`
+ |
+ = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings`
+ = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: import ending with `::{self}`
+ --> $DIR/unnecessary_self_imports.rs:8:1
+ |
+LL | use std::rc::{self};
+ | ^^^^^^^^^-----------
+ | |
+ | help: consider omitting `::{self}`: `rc;`
+ |
+ = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
new file mode 100644
index 000000000..21e2da474
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
@@ -0,0 +1,103 @@
+// run-rustfix
+
+#![allow(clippy::stable_sort_primitive)]
+
+use std::cell::Ref;
+
+fn unnecessary_sort_by() {
+ fn id(x: isize) -> isize {
+ x
+ }
+
+ let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
+ // Forward examples
+ vec.sort();
+ vec.sort_unstable();
+ vec.sort_by_key(|a| (a + 5).abs());
+ vec.sort_unstable_by_key(|a| id(-a));
+ // Reverse examples
+ vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow
+ vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()));
+ vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)));
+ // Negative examples (shouldn't be changed)
+ let c = &7;
+ vec.sort_by(|a, b| (b - a).cmp(&(a - b)));
+ vec.sort_by(|_, b| b.cmp(&5));
+ vec.sort_by(|_, b| b.cmp(c));
+ vec.sort_unstable_by(|a, _| a.cmp(c));
+
+ // Vectors of references are fine as long as the resulting key does not borrow
+ let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
+ vec.sort_by_key(|a| (***a).abs());
+ vec.sort_unstable_by_key(|a| (***a).abs());
+ // `Reverse(b)` would borrow in the following cases, don't lint
+ vec.sort_by(|a, b| b.cmp(a));
+ vec.sort_unstable_by(|a, b| b.cmp(a));
+
+ // No warning if element does not implement `Ord`
+ let mut vec: Vec<Ref<usize>> = Vec::new();
+ vec.sort_unstable_by(|a, b| a.cmp(b));
+}
+
+// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
+mod issue_5754 {
+ #[derive(Clone, Copy)]
+ struct Test(usize);
+
+ #[derive(PartialOrd, Ord, PartialEq, Eq)]
+ struct Wrapper<'a>(&'a usize);
+
+ impl Test {
+ fn name(&self) -> &usize {
+ &self.0
+ }
+
+ fn wrapped(&self) -> Wrapper<'_> {
+ Wrapper(&self.0)
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by(|a, b| a.name().cmp(b.name()));
+ args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+ args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+ args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+ // Reverse
+ args.sort_by(|a, b| b.name().cmp(a.name()));
+ args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+ args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+ args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+ }
+}
+
+// The closure parameter is not dereferenced anymore, so non-Copy types can be linted
+mod issue_6001 {
+ struct Test(String);
+
+ impl Test {
+ // Return an owned type so that we don't hit the fix for 5754
+ fn name(&self) -> String {
+ self.0.clone()
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by_key(|a| a.name());
+ args.sort_unstable_by_key(|a| a.name());
+ // Reverse
+ args.sort_by_key(|b| std::cmp::Reverse(b.name()));
+ args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name()));
+ }
+}
+
+fn main() {
+ unnecessary_sort_by();
+ issue_5754::test();
+ issue_6001::test();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
new file mode 100644
index 000000000..3365bf6e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
@@ -0,0 +1,103 @@
+// run-rustfix
+
+#![allow(clippy::stable_sort_primitive)]
+
+use std::cell::Ref;
+
+fn unnecessary_sort_by() {
+ fn id(x: isize) -> isize {
+ x
+ }
+
+ let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
+ // Forward examples
+ vec.sort_by(|a, b| a.cmp(b));
+ vec.sort_unstable_by(|a, b| a.cmp(b));
+ vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
+ vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
+ // Reverse examples
+ vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow
+ vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
+ vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
+ // Negative examples (shouldn't be changed)
+ let c = &7;
+ vec.sort_by(|a, b| (b - a).cmp(&(a - b)));
+ vec.sort_by(|_, b| b.cmp(&5));
+ vec.sort_by(|_, b| b.cmp(c));
+ vec.sort_unstable_by(|a, _| a.cmp(c));
+
+ // Vectors of references are fine as long as the resulting key does not borrow
+ let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
+ vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ // `Reverse(b)` would borrow in the following cases, don't lint
+ vec.sort_by(|a, b| b.cmp(a));
+ vec.sort_unstable_by(|a, b| b.cmp(a));
+
+ // No warning if element does not implement `Ord`
+ let mut vec: Vec<Ref<usize>> = Vec::new();
+ vec.sort_unstable_by(|a, b| a.cmp(b));
+}
+
+// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
+mod issue_5754 {
+ #[derive(Clone, Copy)]
+ struct Test(usize);
+
+ #[derive(PartialOrd, Ord, PartialEq, Eq)]
+ struct Wrapper<'a>(&'a usize);
+
+ impl Test {
+ fn name(&self) -> &usize {
+ &self.0
+ }
+
+ fn wrapped(&self) -> Wrapper<'_> {
+ Wrapper(&self.0)
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by(|a, b| a.name().cmp(b.name()));
+ args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+ args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+ args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+ // Reverse
+ args.sort_by(|a, b| b.name().cmp(a.name()));
+ args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+ args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+ args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+ }
+}
+
+// The closure parameter is not dereferenced anymore, so non-Copy types can be linted
+mod issue_6001 {
+ struct Test(String);
+
+ impl Test {
+ // Return an owned type so that we don't hit the fix for 5754
+ fn name(&self) -> String {
+ self.0.clone()
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by(|a, b| a.name().cmp(&b.name()));
+ args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
+ // Reverse
+ args.sort_by(|a, b| b.name().cmp(&a.name()));
+ args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
+ }
+}
+
+fn main() {
+ unnecessary_sort_by();
+ issue_5754::test();
+ issue_6001::test();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr
new file mode 100644
index 000000000..89da5e7ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr
@@ -0,0 +1,76 @@
+error: use Vec::sort here instead
+ --> $DIR/unnecessary_sort_by.rs:14:5
+ |
+LL | vec.sort_by(|a, b| a.cmp(b));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()`
+ |
+ = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
+
+error: use Vec::sort here instead
+ --> $DIR/unnecessary_sort_by.rs:15:5
+ |
+LL | vec.sort_unstable_by(|a, b| a.cmp(b));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:16:5
+ |
+LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:17:5
+ |
+LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:20:5
+ |
+LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:21:5
+ |
+LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)))`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:31:5
+ |
+LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:32:5
+ |
+LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:91:9
+ |
+LL | args.sort_by(|a, b| a.name().cmp(&b.name()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:92:9
+ |
+LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:94:9
+ |
+LL | args.sort_by(|a, b| b.name().cmp(&a.name()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))`
+
+error: use Vec::sort_by_key here instead
+ --> $DIR/unnecessary_sort_by.rs:95:9
+ |
+LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name()))`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
new file mode 100644
index 000000000..f4f76cd3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -0,0 +1,331 @@
+// run-rustfix
+
+#![allow(clippy::ptr_arg)]
+#![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
+
+use std::borrow::Cow;
+use std::ffi::{CStr, CString, OsStr, OsString};
+use std::ops::Deref;
+
+#[derive(Clone)]
+struct X(String);
+
+impl Deref for X {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ self.0.as_bytes()
+ }
+}
+
+impl AsRef<str> for X {
+ fn as_ref(&self) -> &str {
+ self.0.as_str()
+ }
+}
+
+impl ToString for X {
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+}
+
+impl X {
+ fn join(&self, other: impl AsRef<str>) -> Self {
+ let mut s = self.0.clone();
+ s.push_str(other.as_ref());
+ Self(s)
+ }
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+enum FileType {
+ Account,
+ PrivateKey,
+ Certificate,
+}
+
+fn main() {
+ let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
+ let os_str = OsStr::new("x");
+ let path = std::path::Path::new("x");
+ let s = "x";
+ let array = ["x"];
+ let array_ref = &["x"];
+ let slice = &["x"][..];
+ let x = X(String::from("x"));
+ let x_ref = &x;
+
+ require_c_str(&Cow::from(c_str));
+ require_c_str(c_str);
+
+ require_os_str(os_str);
+ require_os_str(&Cow::from(os_str));
+ require_os_str(os_str);
+
+ require_path(path);
+ require_path(&Cow::from(path));
+ require_path(path);
+
+ require_str(s);
+ require_str(&Cow::from(s));
+ require_str(s);
+ require_str(x_ref.as_ref());
+
+ require_slice(slice);
+ require_slice(&Cow::from(slice));
+ require_slice(array.as_ref());
+ require_slice(array_ref.as_ref());
+ require_slice(slice);
+ require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
+
+ require_x(&Cow::<X>::Owned(x.clone()));
+ require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
+
+ require_deref_c_str(c_str);
+ require_deref_os_str(os_str);
+ require_deref_path(path);
+ require_deref_str(s);
+ require_deref_slice(slice);
+
+ require_impl_deref_c_str(c_str);
+ require_impl_deref_os_str(os_str);
+ require_impl_deref_path(path);
+ require_impl_deref_str(s);
+ require_impl_deref_slice(slice);
+
+ require_deref_str_slice(s, slice);
+ require_deref_slice_str(slice, s);
+
+ require_as_ref_c_str(c_str);
+ require_as_ref_os_str(os_str);
+ require_as_ref_path(path);
+ require_as_ref_str(s);
+ require_as_ref_str(&x);
+ require_as_ref_slice(array);
+ require_as_ref_slice(array_ref);
+ require_as_ref_slice(slice);
+
+ require_impl_as_ref_c_str(c_str);
+ require_impl_as_ref_os_str(os_str);
+ require_impl_as_ref_path(path);
+ require_impl_as_ref_str(s);
+ require_impl_as_ref_str(&x);
+ require_impl_as_ref_slice(array);
+ require_impl_as_ref_slice(array_ref);
+ require_impl_as_ref_slice(slice);
+
+ require_as_ref_str_slice(s, array);
+ require_as_ref_str_slice(s, array_ref);
+ require_as_ref_str_slice(s, slice);
+ require_as_ref_slice_str(array, s);
+ require_as_ref_slice_str(array_ref, s);
+ require_as_ref_slice_str(slice, s);
+
+ let _ = x.join(x_ref);
+
+ let _ = slice.iter().copied();
+ let _ = slice.iter().copied();
+ let _ = [std::path::PathBuf::new()][..].iter().cloned();
+ let _ = [std::path::PathBuf::new()][..].iter().cloned();
+
+ let _ = slice.iter().copied();
+ let _ = slice.iter().copied();
+ let _ = [std::path::PathBuf::new()][..].iter().cloned();
+ let _ = [std::path::PathBuf::new()][..].iter().cloned();
+
+ let _ = check_files(&[FileType::Account]);
+
+ // negative tests
+ require_string(&s.to_string());
+ require_string(&Cow::from(s).into_owned());
+ require_string(&s.to_owned());
+ require_string(&x_ref.to_string());
+
+ // `X` isn't copy.
+ require_slice(&x.to_owned());
+ require_deref_slice(x.to_owned());
+
+ // The following should be flagged by `redundant_clone`, but not by this lint.
+ require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
+ require_os_str(&OsString::from("x"));
+ require_path(&std::path::PathBuf::from("x"));
+ require_str(&String::from("x"));
+ require_slice(&[String::from("x")]);
+}
+
+fn require_c_str(_: &CStr) {}
+fn require_os_str(_: &OsStr) {}
+fn require_path(_: &std::path::Path) {}
+fn require_str(_: &str) {}
+fn require_slice<T>(_: &[T]) {}
+fn require_x(_: &X) {}
+
+fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
+fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
+fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
+fn require_deref_str<T: Deref<Target = str>>(_: T) {}
+fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
+
+fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
+fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
+fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
+fn require_impl_deref_str(_: impl Deref<Target = str>) {}
+fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
+
+fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
+fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
+
+fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
+fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
+fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
+fn require_as_ref_str<T: AsRef<str>>(_: T) {}
+fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
+
+fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
+fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
+fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
+fn require_impl_as_ref_str(_: impl AsRef<str>) {}
+fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
+
+fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
+fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
+
+// `check_files` is based on:
+// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
+fn check_files(file_types: &[FileType]) -> bool {
+ for t in file_types {
+ let path = match get_file_path(t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
+ Ok(std::path::PathBuf::new())
+}
+
+fn require_string(_: &String) {}
+
+fn _msrv_1_35() {
+ #![clippy::msrv = "1.35"]
+ // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+ let _ = &["x"][..].iter().cloned();
+}
+
+fn _msrv_1_36() {
+ #![clippy::msrv = "1.36"]
+ let _ = &["x"][..].iter().copied();
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8507
+mod issue_8507 {
+ #![allow(dead_code)]
+
+ struct Opaque<P>(P);
+
+ pub trait Abstracted {}
+
+ impl<P> Abstracted for Opaque<P> {}
+
+ fn build<P>(p: P) -> Opaque<P>
+ where
+ P: AsRef<str>,
+ {
+ Opaque(p)
+ }
+
+ // Should not lint.
+ fn test_str(s: &str) -> Box<dyn Abstracted> {
+ Box::new(build(s.to_string()))
+ }
+
+ // Should not lint.
+ fn test_x(x: super::X) -> Box<dyn Abstracted> {
+ Box::new(build(x))
+ }
+
+ #[derive(Clone, Copy)]
+ struct Y(&'static str);
+
+ impl AsRef<str> for Y {
+ fn as_ref(&self) -> &str {
+ self.0
+ }
+ }
+
+ impl ToString for Y {
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+ }
+
+ // Should lint because Y is copy.
+ fn test_y(y: Y) -> Box<dyn Abstracted> {
+ Box::new(build(y))
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8759
+mod issue_8759 {
+ #![allow(dead_code)]
+
+ #[derive(Default)]
+ struct View {}
+
+ impl std::borrow::ToOwned for View {
+ type Owned = View;
+ fn to_owned(&self) -> Self::Owned {
+ View {}
+ }
+ }
+
+ #[derive(Default)]
+ struct RenderWindow {
+ default_view: View,
+ }
+
+ impl RenderWindow {
+ fn default_view(&self) -> &View {
+ &self.default_view
+ }
+ fn set_view(&mut self, _view: &View) {}
+ }
+
+ fn main() {
+ let mut rw = RenderWindow::default();
+ rw.set_view(&rw.default_view().to_owned());
+ }
+}
+
+mod issue_8759_variant {
+ #![allow(dead_code)]
+
+ #[derive(Clone, Default)]
+ struct View {}
+
+ #[derive(Default)]
+ struct RenderWindow {
+ default_view: View,
+ }
+
+ impl RenderWindow {
+ fn default_view(&self) -> &View {
+ &self.default_view
+ }
+ fn set_view(&mut self, _view: &View) {}
+ }
+
+ fn main() {
+ let mut rw = RenderWindow::default();
+ rw.set_view(&rw.default_view().to_owned());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
new file mode 100644
index 000000000..fe09a489a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -0,0 +1,331 @@
+// run-rustfix
+
+#![allow(clippy::ptr_arg)]
+#![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
+
+use std::borrow::Cow;
+use std::ffi::{CStr, CString, OsStr, OsString};
+use std::ops::Deref;
+
+#[derive(Clone)]
+struct X(String);
+
+impl Deref for X {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ self.0.as_bytes()
+ }
+}
+
+impl AsRef<str> for X {
+ fn as_ref(&self) -> &str {
+ self.0.as_str()
+ }
+}
+
+impl ToString for X {
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+}
+
+impl X {
+ fn join(&self, other: impl AsRef<str>) -> Self {
+ let mut s = self.0.clone();
+ s.push_str(other.as_ref());
+ Self(s)
+ }
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+enum FileType {
+ Account,
+ PrivateKey,
+ Certificate,
+}
+
+fn main() {
+ let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
+ let os_str = OsStr::new("x");
+ let path = std::path::Path::new("x");
+ let s = "x";
+ let array = ["x"];
+ let array_ref = &["x"];
+ let slice = &["x"][..];
+ let x = X(String::from("x"));
+ let x_ref = &x;
+
+ require_c_str(&Cow::from(c_str).into_owned());
+ require_c_str(&c_str.to_owned());
+
+ require_os_str(&os_str.to_os_string());
+ require_os_str(&Cow::from(os_str).into_owned());
+ require_os_str(&os_str.to_owned());
+
+ require_path(&path.to_path_buf());
+ require_path(&Cow::from(path).into_owned());
+ require_path(&path.to_owned());
+
+ require_str(&s.to_string());
+ require_str(&Cow::from(s).into_owned());
+ require_str(&s.to_owned());
+ require_str(&x_ref.to_string());
+
+ require_slice(&slice.to_vec());
+ require_slice(&Cow::from(slice).into_owned());
+ require_slice(&array.to_owned());
+ require_slice(&array_ref.to_owned());
+ require_slice(&slice.to_owned());
+ require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
+
+ require_x(&Cow::<X>::Owned(x.clone()).into_owned());
+ require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
+
+ require_deref_c_str(c_str.to_owned());
+ require_deref_os_str(os_str.to_owned());
+ require_deref_path(path.to_owned());
+ require_deref_str(s.to_owned());
+ require_deref_slice(slice.to_owned());
+
+ require_impl_deref_c_str(c_str.to_owned());
+ require_impl_deref_os_str(os_str.to_owned());
+ require_impl_deref_path(path.to_owned());
+ require_impl_deref_str(s.to_owned());
+ require_impl_deref_slice(slice.to_owned());
+
+ require_deref_str_slice(s.to_owned(), slice.to_owned());
+ require_deref_slice_str(slice.to_owned(), s.to_owned());
+
+ require_as_ref_c_str(c_str.to_owned());
+ require_as_ref_os_str(os_str.to_owned());
+ require_as_ref_path(path.to_owned());
+ require_as_ref_str(s.to_owned());
+ require_as_ref_str(x.to_owned());
+ require_as_ref_slice(array.to_owned());
+ require_as_ref_slice(array_ref.to_owned());
+ require_as_ref_slice(slice.to_owned());
+
+ require_impl_as_ref_c_str(c_str.to_owned());
+ require_impl_as_ref_os_str(os_str.to_owned());
+ require_impl_as_ref_path(path.to_owned());
+ require_impl_as_ref_str(s.to_owned());
+ require_impl_as_ref_str(x.to_owned());
+ require_impl_as_ref_slice(array.to_owned());
+ require_impl_as_ref_slice(array_ref.to_owned());
+ require_impl_as_ref_slice(slice.to_owned());
+
+ require_as_ref_str_slice(s.to_owned(), array.to_owned());
+ require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
+ require_as_ref_str_slice(s.to_owned(), slice.to_owned());
+ require_as_ref_slice_str(array.to_owned(), s.to_owned());
+ require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
+ require_as_ref_slice_str(slice.to_owned(), s.to_owned());
+
+ let _ = x.join(&x_ref.to_string());
+
+ let _ = slice.to_vec().into_iter();
+ let _ = slice.to_owned().into_iter();
+ let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
+ let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
+
+ let _ = IntoIterator::into_iter(slice.to_vec());
+ let _ = IntoIterator::into_iter(slice.to_owned());
+ let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
+ let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
+
+ let _ = check_files(&[FileType::Account]);
+
+ // negative tests
+ require_string(&s.to_string());
+ require_string(&Cow::from(s).into_owned());
+ require_string(&s.to_owned());
+ require_string(&x_ref.to_string());
+
+ // `X` isn't copy.
+ require_slice(&x.to_owned());
+ require_deref_slice(x.to_owned());
+
+ // The following should be flagged by `redundant_clone`, but not by this lint.
+ require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
+ require_os_str(&OsString::from("x").to_os_string());
+ require_path(&std::path::PathBuf::from("x").to_path_buf());
+ require_str(&String::from("x").to_string());
+ require_slice(&[String::from("x")].to_owned());
+}
+
+fn require_c_str(_: &CStr) {}
+fn require_os_str(_: &OsStr) {}
+fn require_path(_: &std::path::Path) {}
+fn require_str(_: &str) {}
+fn require_slice<T>(_: &[T]) {}
+fn require_x(_: &X) {}
+
+fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
+fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
+fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
+fn require_deref_str<T: Deref<Target = str>>(_: T) {}
+fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
+
+fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
+fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
+fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
+fn require_impl_deref_str(_: impl Deref<Target = str>) {}
+fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
+
+fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
+fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
+
+fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
+fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
+fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
+fn require_as_ref_str<T: AsRef<str>>(_: T) {}
+fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
+
+fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
+fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
+fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
+fn require_impl_as_ref_str(_: impl AsRef<str>) {}
+fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
+
+fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
+fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
+
+// `check_files` is based on:
+// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
+fn check_files(file_types: &[FileType]) -> bool {
+ for t in file_types.to_vec() {
+ let path = match get_file_path(&t) {
+ Ok(p) => p,
+ Err(_) => {
+ return false;
+ },
+ };
+ if !path.is_file() {
+ return false;
+ }
+ }
+ true
+}
+
+fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
+ Ok(std::path::PathBuf::new())
+}
+
+fn require_string(_: &String) {}
+
+fn _msrv_1_35() {
+ #![clippy::msrv = "1.35"]
+ // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+ let _ = &["x"][..].to_vec().into_iter();
+}
+
+fn _msrv_1_36() {
+ #![clippy::msrv = "1.36"]
+ let _ = &["x"][..].to_vec().into_iter();
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8507
+mod issue_8507 {
+ #![allow(dead_code)]
+
+ struct Opaque<P>(P);
+
+ pub trait Abstracted {}
+
+ impl<P> Abstracted for Opaque<P> {}
+
+ fn build<P>(p: P) -> Opaque<P>
+ where
+ P: AsRef<str>,
+ {
+ Opaque(p)
+ }
+
+ // Should not lint.
+ fn test_str(s: &str) -> Box<dyn Abstracted> {
+ Box::new(build(s.to_string()))
+ }
+
+ // Should not lint.
+ fn test_x(x: super::X) -> Box<dyn Abstracted> {
+ Box::new(build(x))
+ }
+
+ #[derive(Clone, Copy)]
+ struct Y(&'static str);
+
+ impl AsRef<str> for Y {
+ fn as_ref(&self) -> &str {
+ self.0
+ }
+ }
+
+ impl ToString for Y {
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+ }
+
+ // Should lint because Y is copy.
+ fn test_y(y: Y) -> Box<dyn Abstracted> {
+ Box::new(build(y.to_string()))
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8759
+mod issue_8759 {
+ #![allow(dead_code)]
+
+ #[derive(Default)]
+ struct View {}
+
+ impl std::borrow::ToOwned for View {
+ type Owned = View;
+ fn to_owned(&self) -> Self::Owned {
+ View {}
+ }
+ }
+
+ #[derive(Default)]
+ struct RenderWindow {
+ default_view: View,
+ }
+
+ impl RenderWindow {
+ fn default_view(&self) -> &View {
+ &self.default_view
+ }
+ fn set_view(&mut self, _view: &View) {}
+ }
+
+ fn main() {
+ let mut rw = RenderWindow::default();
+ rw.set_view(&rw.default_view().to_owned());
+ }
+}
+
+mod issue_8759_variant {
+ #![allow(dead_code)]
+
+ #[derive(Clone, Default)]
+ struct View {}
+
+ #[derive(Default)]
+ struct RenderWindow {
+ default_view: View,
+ }
+
+ impl RenderWindow {
+ fn default_view(&self) -> &View {
+ &self.default_view
+ }
+ fn set_view(&mut self, _view: &View) {}
+ }
+
+ fn main() {
+ let mut rw = RenderWindow::default();
+ rw.set_view(&rw.default_view().to_owned());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
new file mode 100644
index 000000000..243b4599d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -0,0 +1,513 @@
+error: redundant clone
+ --> $DIR/unnecessary_to_owned.rs:151:64
+ |
+LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
+ | ^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::redundant-clone` implied by `-D warnings`
+note: this value is dropped without further use
+ --> $DIR/unnecessary_to_owned.rs:151:20
+ |
+LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/unnecessary_to_owned.rs:152:40
+ |
+LL | require_os_str(&OsString::from("x").to_os_string());
+ | ^^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/unnecessary_to_owned.rs:152:21
+ |
+LL | require_os_str(&OsString::from("x").to_os_string());
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/unnecessary_to_owned.rs:153:48
+ |
+LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
+ | ^^^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/unnecessary_to_owned.rs:153:19
+ |
+LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/unnecessary_to_owned.rs:154:35
+ |
+LL | require_str(&String::from("x").to_string());
+ | ^^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/unnecessary_to_owned.rs:154:18
+ |
+LL | require_str(&String::from("x").to_string());
+ | ^^^^^^^^^^^^^^^^^
+
+error: redundant clone
+ --> $DIR/unnecessary_to_owned.rs:155:39
+ |
+LL | require_slice(&[String::from("x")].to_owned());
+ | ^^^^^^^^^^^ help: remove this
+ |
+note: this value is dropped without further use
+ --> $DIR/unnecessary_to_owned.rs:155:20
+ |
+LL | require_slice(&[String::from("x")].to_owned());
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:60:36
+ |
+LL | require_c_str(&Cow::from(c_str).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+ |
+ = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:61:19
+ |
+LL | require_c_str(&c_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `c_str`
+
+error: unnecessary use of `to_os_string`
+ --> $DIR/unnecessary_to_owned.rs:63:20
+ |
+LL | require_os_str(&os_str.to_os_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:64:38
+ |
+LL | require_os_str(&Cow::from(os_str).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:65:20
+ |
+LL | require_os_str(&os_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `to_path_buf`
+ --> $DIR/unnecessary_to_owned.rs:67:18
+ |
+LL | require_path(&path.to_path_buf());
+ | ^^^^^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:68:34
+ |
+LL | require_path(&Cow::from(path).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:69:18
+ |
+LL | require_path(&path.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `to_string`
+ --> $DIR/unnecessary_to_owned.rs:71:17
+ |
+LL | require_str(&s.to_string());
+ | ^^^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:72:30
+ |
+LL | require_str(&Cow::from(s).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:73:17
+ |
+LL | require_str(&s.to_owned());
+ | ^^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_string`
+ --> $DIR/unnecessary_to_owned.rs:74:17
+ |
+LL | require_str(&x_ref.to_string());
+ | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:76:19
+ |
+LL | require_slice(&slice.to_vec());
+ | ^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:77:36
+ |
+LL | require_slice(&Cow::from(slice).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:78:19
+ |
+LL | require_slice(&array.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:79:19
+ |
+LL | require_slice(&array_ref.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:80:19
+ |
+LL | require_slice(&slice.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `into_owned`
+ --> $DIR/unnecessary_to_owned.rs:83:42
+ |
+LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned());
+ | ^^^^^^^^^^^^^ help: remove this
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:86:25
+ |
+LL | require_deref_c_str(c_str.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `c_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:87:26
+ |
+LL | require_deref_os_str(os_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:88:24
+ |
+LL | require_deref_path(path.to_owned());
+ | ^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:89:23
+ |
+LL | require_deref_str(s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:90:25
+ |
+LL | require_deref_slice(slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:92:30
+ |
+LL | require_impl_deref_c_str(c_str.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `c_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:93:31
+ |
+LL | require_impl_deref_os_str(os_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:94:29
+ |
+LL | require_impl_deref_path(path.to_owned());
+ | ^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:95:28
+ |
+LL | require_impl_deref_str(s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:96:30
+ |
+LL | require_impl_deref_slice(slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:98:29
+ |
+LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:98:43
+ |
+LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:99:29
+ |
+LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:99:47
+ |
+LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:101:26
+ |
+LL | require_as_ref_c_str(c_str.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `c_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:102:27
+ |
+LL | require_as_ref_os_str(os_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:103:25
+ |
+LL | require_as_ref_path(path.to_owned());
+ | ^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:104:24
+ |
+LL | require_as_ref_str(s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:105:24
+ |
+LL | require_as_ref_str(x.to_owned());
+ | ^^^^^^^^^^^^ help: use: `&x`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:106:26
+ |
+LL | require_as_ref_slice(array.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `array`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:107:26
+ |
+LL | require_as_ref_slice(array_ref.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:108:26
+ |
+LL | require_as_ref_slice(slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:110:31
+ |
+LL | require_impl_as_ref_c_str(c_str.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `c_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:111:32
+ |
+LL | require_impl_as_ref_os_str(os_str.to_owned());
+ | ^^^^^^^^^^^^^^^^^ help: use: `os_str`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:112:30
+ |
+LL | require_impl_as_ref_path(path.to_owned());
+ | ^^^^^^^^^^^^^^^ help: use: `path`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:113:29
+ |
+LL | require_impl_as_ref_str(s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:114:29
+ |
+LL | require_impl_as_ref_str(x.to_owned());
+ | ^^^^^^^^^^^^ help: use: `&x`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:115:31
+ |
+LL | require_impl_as_ref_slice(array.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `array`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:116:31
+ |
+LL | require_impl_as_ref_slice(array_ref.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:117:31
+ |
+LL | require_impl_as_ref_slice(slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:119:30
+ |
+LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:119:44
+ |
+LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `array`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:120:30
+ |
+LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:120:44
+ |
+LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:121:30
+ |
+LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:121:44
+ |
+LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:122:30
+ |
+LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `array`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:122:48
+ |
+LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:123:30
+ |
+LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:123:52
+ |
+LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:124:30
+ |
+LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^^^^^ help: use: `slice`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:124:48
+ |
+LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
+ | ^^^^^^^^^^^^ help: use: `s`
+
+error: unnecessary use of `to_string`
+ --> $DIR/unnecessary_to_owned.rs:126:20
+ |
+LL | let _ = x.join(&x_ref.to_string());
+ | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:128:13
+ |
+LL | let _ = slice.to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:129:13
+ |
+LL | let _ = slice.to_owned().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:130:13
+ |
+LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:131:13
+ |
+LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:133:13
+ |
+LL | let _ = IntoIterator::into_iter(slice.to_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:134:13
+ |
+LL | let _ = IntoIterator::into_iter(slice.to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:135:13
+ |
+LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
+
+error: unnecessary use of `to_owned`
+ --> $DIR/unnecessary_to_owned.rs:136:13
+ |
+LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:198:14
+ |
+LL | for t in file_types.to_vec() {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: use
+ |
+LL | for t in file_types {
+ | ~~~~~~~~~~
+help: remove this `&`
+ |
+LL - let path = match get_file_path(&t) {
+LL + let path = match get_file_path(t) {
+ |
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:221:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:226:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
+
+error: unnecessary use of `to_string`
+ --> $DIR/unnecessary_to_owned.rs:273:24
+ |
+LL | Box::new(build(y.to_string()))
+ | ^^^^^^^^^^^^^ help: use: `y`
+
+error: aborting due to 78 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.rs b/src/tools/clippy/tests/ui/unnecessary_wraps.rs
new file mode 100644
index 000000000..63648ef58
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_wraps.rs
@@ -0,0 +1,144 @@
+#![warn(clippy::unnecessary_wraps)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::needless_return)]
+#![allow(clippy::if_same_then_else)]
+#![allow(dead_code)]
+
+// should be linted
+fn func1(a: bool, b: bool) -> Option<i32> {
+ if a && b {
+ return Some(42);
+ }
+ if a {
+ Some(-1);
+ Some(2)
+ } else {
+ return Some(1337);
+ }
+}
+
+// should be linted
+fn func2(a: bool, b: bool) -> Option<i32> {
+ if a && b {
+ return Some(10);
+ }
+ if a { Some(20) } else { Some(30) }
+}
+
+// public fns should not be linted
+pub fn func3(a: bool) -> Option<i32> {
+ if a { Some(1) } else { Some(1) }
+}
+
+// should not be linted
+fn func4(a: bool) -> Option<i32> {
+ if a { Some(1) } else { None }
+}
+
+// should be linted
+fn func5() -> Option<i32> {
+ Some(1)
+}
+
+// should not be linted
+fn func6() -> Option<i32> {
+ None
+}
+
+// should be linted
+fn func7() -> Result<i32, ()> {
+ Ok(1)
+}
+
+// should not be linted
+fn func8(a: bool) -> Result<i32, ()> {
+ if a { Ok(1) } else { Err(()) }
+}
+
+// should not be linted
+fn func9(a: bool) -> Result<i32, ()> {
+ Err(())
+}
+
+// should not be linted
+fn func10() -> Option<()> {
+ unimplemented!()
+}
+
+pub struct A;
+
+impl A {
+ // should not be linted
+ pub fn func11() -> Option<i32> {
+ Some(1)
+ }
+
+ // should be linted
+ fn func12() -> Option<i32> {
+ Some(1)
+ }
+}
+
+trait B {
+ // trait impls are not linted
+ fn func13() -> Option<i32> {
+ Some(1)
+ }
+}
+
+impl B for A {
+ // trait impls are not linted
+ fn func13() -> Option<i32> {
+ Some(0)
+ }
+}
+
+fn issue_6384(s: &str) -> Option<&str> {
+ Some(match s {
+ "a" => "A",
+ _ => return None,
+ })
+}
+
+// should be linted
+fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+ if a && b {
+ return Some(());
+ }
+ if a {
+ Some(());
+ Some(())
+ } else {
+ return Some(());
+ }
+}
+
+// should be linted
+fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+ if a && b {
+ return Ok(());
+ }
+ if a {
+ Ok(())
+ } else {
+ return Ok(());
+ }
+}
+
+// should not be linted
+fn issue_6640_3() -> Option<()> {
+ if true { Some(()) } else { None }
+}
+
+// should not be linted
+fn issue_6640_4() -> Result<(), ()> {
+ if true { Ok(()) } else { Err(()) }
+}
+
+fn main() {
+ // method calls are not linted
+ func1(true, true);
+ func2(true, true);
+ issue_6640_1(true, true);
+ issue_6640_2(true, true);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr
new file mode 100644
index 000000000..a6a0b22cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr
@@ -0,0 +1,156 @@
+error: this function's return value is unnecessarily wrapped by `Option`
+ --> $DIR/unnecessary_wraps.rs:8:1
+ |
+LL | / fn func1(a: bool, b: bool) -> Option<i32> {
+LL | | if a && b {
+LL | | return Some(42);
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::unnecessary-wraps` implied by `-D warnings`
+help: remove `Option` from the return type...
+ |
+LL | fn func1(a: bool, b: bool) -> i32 {
+ | ~~~
+help: ...and then change returning expressions
+ |
+LL ~ return 42;
+LL | }
+LL | if a {
+LL | Some(-1);
+LL ~ 2
+LL | } else {
+LL ~ return 1337;
+ |
+
+error: this function's return value is unnecessarily wrapped by `Option`
+ --> $DIR/unnecessary_wraps.rs:21:1
+ |
+LL | / fn func2(a: bool, b: bool) -> Option<i32> {
+LL | | if a && b {
+LL | | return Some(10);
+LL | | }
+LL | | if a { Some(20) } else { Some(30) }
+LL | | }
+ | |_^
+ |
+help: remove `Option` from the return type...
+ |
+LL | fn func2(a: bool, b: bool) -> i32 {
+ | ~~~
+help: ...and then change returning expressions
+ |
+LL ~ return 10;
+LL | }
+LL ~ if a { 20 } else { 30 }
+ |
+
+error: this function's return value is unnecessarily wrapped by `Option`
+ --> $DIR/unnecessary_wraps.rs:39:1
+ |
+LL | / fn func5() -> Option<i32> {
+LL | | Some(1)
+LL | | }
+ | |_^
+ |
+help: remove `Option` from the return type...
+ |
+LL | fn func5() -> i32 {
+ | ~~~
+help: ...and then change returning expressions
+ |
+LL | 1
+ |
+
+error: this function's return value is unnecessarily wrapped by `Result`
+ --> $DIR/unnecessary_wraps.rs:49:1
+ |
+LL | / fn func7() -> Result<i32, ()> {
+LL | | Ok(1)
+LL | | }
+ | |_^
+ |
+help: remove `Result` from the return type...
+ |
+LL | fn func7() -> i32 {
+ | ~~~
+help: ...and then change returning expressions
+ |
+LL | 1
+ |
+
+error: this function's return value is unnecessarily wrapped by `Option`
+ --> $DIR/unnecessary_wraps.rs:77:5
+ |
+LL | / fn func12() -> Option<i32> {
+LL | | Some(1)
+LL | | }
+ | |_____^
+ |
+help: remove `Option` from the return type...
+ |
+LL | fn func12() -> i32 {
+ | ~~~
+help: ...and then change returning expressions
+ |
+LL | 1
+ |
+
+error: this function's return value is unnecessary
+ --> $DIR/unnecessary_wraps.rs:104:1
+ |
+LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+LL | | if a && b {
+LL | | return Some(());
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: remove the return type...
+ |
+LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+ | ~~~~~~~~~~
+help: ...and then remove returned values
+ |
+LL ~ return ;
+LL | }
+LL | if a {
+LL | Some(());
+LL ~
+LL | } else {
+LL ~ return ;
+ |
+
+error: this function's return value is unnecessary
+ --> $DIR/unnecessary_wraps.rs:117:1
+ |
+LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+LL | | if a && b {
+LL | | return Ok(());
+LL | | }
+... |
+LL | | }
+LL | | }
+ | |_^
+ |
+help: remove the return type...
+ |
+LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+ | ~~~~~~~~~~~~~~~
+help: ...and then remove returned values
+ |
+LL ~ return ;
+LL | }
+LL | if a {
+LL ~
+LL | } else {
+LL ~ return ;
+ |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs
new file mode 100644
index 000000000..fa639aa70
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::unneeded_field_pattern)]
+#[allow(dead_code, unused)]
+
+struct Foo {
+ a: i32,
+ b: i32,
+ c: i32,
+}
+
+fn main() {
+ let f = Foo { a: 0, b: 0, c: 0 };
+
+ match f {
+ Foo { a: _, b: 0, .. } => {},
+
+ Foo { a: _, b: _, c: _ } => {},
+ }
+ match f {
+ Foo { b: 0, .. } => {}, // should be OK
+ Foo { .. } => {}, // and the Force might be with this one
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
new file mode 100644
index 000000000..b8d3c2945
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
@@ -0,0 +1,19 @@
+error: you matched a field with a wildcard pattern, consider using `..` instead
+ --> $DIR/unneeded_field_pattern.rs:14:15
+ |
+LL | Foo { a: _, b: 0, .. } => {},
+ | ^^^^
+ |
+ = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
+ = help: try with `Foo { b: 0, .. }`
+
+error: all the struct fields are matched to a wildcard pattern, consider using `..`
+ --> $DIR/unneeded_field_pattern.rs:16:9
+ |
+LL | Foo { a: _, b: _, c: _ } => {},
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try with `Foo { .. }` instead
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
new file mode 100644
index 000000000..12c3461c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+#![deny(clippy::unneeded_wildcard_pattern)]
+
+fn main() {
+ let t = (0, 1, 2, 3);
+
+ if let (0, ..) = t {};
+ if let (0, ..) = t {};
+ if let (.., 0) = t {};
+ if let (.., 0) = t {};
+ if let (0, ..) = t {};
+ if let (0, ..) = t {};
+ if let (_, 0, ..) = t {};
+ if let (.., 0, _) = t {};
+ if let (0, _, _, _) = t {};
+ if let (0, ..) = t {};
+ if let (.., 0) = t {};
+
+ #[rustfmt::skip]
+ {
+ if let (0, ..,) = t {};
+ }
+
+ struct S(usize, usize, usize, usize);
+
+ let s = S(0, 1, 2, 3);
+
+ if let S(0, ..) = s {};
+ if let S(0, ..) = s {};
+ if let S(.., 0) = s {};
+ if let S(.., 0) = s {};
+ if let S(0, ..) = s {};
+ if let S(0, ..) = s {};
+ if let S(_, 0, ..) = s {};
+ if let S(.., 0, _) = s {};
+ if let S(0, _, _, _) = s {};
+ if let S(0, ..) = s {};
+ if let S(.., 0) = s {};
+
+ #[rustfmt::skip]
+ {
+ if let S(0, ..,) = s {};
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
new file mode 100644
index 000000000..4ac01d5d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+#![feature(stmt_expr_attributes)]
+#![deny(clippy::unneeded_wildcard_pattern)]
+
+fn main() {
+ let t = (0, 1, 2, 3);
+
+ if let (0, .., _) = t {};
+ if let (0, _, ..) = t {};
+ if let (_, .., 0) = t {};
+ if let (.., _, 0) = t {};
+ if let (0, _, _, ..) = t {};
+ if let (0, .., _, _) = t {};
+ if let (_, 0, ..) = t {};
+ if let (.., 0, _) = t {};
+ if let (0, _, _, _) = t {};
+ if let (0, ..) = t {};
+ if let (.., 0) = t {};
+
+ #[rustfmt::skip]
+ {
+ if let (0, .., _, _,) = t {};
+ }
+
+ struct S(usize, usize, usize, usize);
+
+ let s = S(0, 1, 2, 3);
+
+ if let S(0, .., _) = s {};
+ if let S(0, _, ..) = s {};
+ if let S(_, .., 0) = s {};
+ if let S(.., _, 0) = s {};
+ if let S(0, _, _, ..) = s {};
+ if let S(0, .., _, _) = s {};
+ if let S(_, 0, ..) = s {};
+ if let S(.., 0, _) = s {};
+ if let S(0, _, _, _) = s {};
+ if let S(0, ..) = s {};
+ if let S(.., 0) = s {};
+
+ #[rustfmt::skip]
+ {
+ if let S(0, .., _, _,) = s {};
+ }
+}
diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
new file mode 100644
index 000000000..716d9ecff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
@@ -0,0 +1,92 @@
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:8:18
+ |
+LL | if let (0, .., _) = t {};
+ | ^^^ help: remove it
+ |
+note: the lint level is defined here
+ --> $DIR/unneeded_wildcard_pattern.rs:3:9
+ |
+LL | #![deny(clippy::unneeded_wildcard_pattern)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:9:16
+ |
+LL | if let (0, _, ..) = t {};
+ | ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:10:13
+ |
+LL | if let (_, .., 0) = t {};
+ | ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:11:15
+ |
+LL | if let (.., _, 0) = t {};
+ | ^^^ help: remove it
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:12:16
+ |
+LL | if let (0, _, _, ..) = t {};
+ | ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:13:18
+ |
+LL | if let (0, .., _, _) = t {};
+ | ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:22:22
+ |
+LL | if let (0, .., _, _,) = t {};
+ | ^^^^^^ help: remove them
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:29:19
+ |
+LL | if let S(0, .., _) = s {};
+ | ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:30:17
+ |
+LL | if let S(0, _, ..) = s {};
+ | ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:31:14
+ |
+LL | if let S(_, .., 0) = s {};
+ | ^^^ help: remove it
+
+error: this pattern is unneeded as the `..` pattern can match that element
+ --> $DIR/unneeded_wildcard_pattern.rs:32:16
+ |
+LL | if let S(.., _, 0) = s {};
+ | ^^^ help: remove it
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:33:17
+ |
+LL | if let S(0, _, _, ..) = s {};
+ | ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:34:19
+ |
+LL | if let S(0, .., _, _) = s {};
+ | ^^^^^^ help: remove them
+
+error: these patterns are unneeded as the `..` pattern can match those elements
+ --> $DIR/unneeded_wildcard_pattern.rs:43:23
+ |
+LL | if let S(0, .., _, _,) = s {};
+ | ^^^^^^ help: remove them
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
new file mode 100644
index 000000000..c223b5bc7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![feature(box_patterns)]
+#![warn(clippy::unnested_or_patterns)]
+#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
+#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
+
+fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
+ if let box (0 | 2) = Box::new(0) {}
+ if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+ const C0: Option<u8> = Some(1);
+ if let Some(1 | 2) | C0 = None {}
+ if let &mut (0 | 2) = &mut 0 {}
+ if let x @ (0 | 2) = 0 {}
+ if let (0, 1 | 2 | 3) = (0, 0) {}
+ if let (1 | 2 | 3, 0) = (0, 0) {}
+ if let (x, ..) | (x, 1 | 2) = (0, 1) {}
+ if let [0 | 1] = [0] {}
+ if let [x, 0 | 1] = [0, 1] {}
+ if let [x, 0 | 1 | 2] = [0, 1] {}
+ if let [x, ..] | [x, 1 | 2] = [0, 1] {}
+ struct TS(u8, u8);
+ if let TS(0 | 1, x) = TS(0, 0) {}
+ if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
+ if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
+ struct S {
+ x: u8,
+ y: u8,
+ }
+ if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
+ if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
new file mode 100644
index 000000000..04cd11036
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![feature(box_patterns)]
+#![warn(clippy::unnested_or_patterns)]
+#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
+#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
+
+fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
+ if let box 0 | box 2 = Box::new(0) {}
+ if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
+ const C0: Option<u8> = Some(1);
+ if let Some(1) | C0 | Some(2) = None {}
+ if let &mut 0 | &mut 2 = &mut 0 {}
+ if let x @ 0 | x @ 2 = 0 {}
+ if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
+ if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
+ if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
+ if let [0] | [1] = [0] {}
+ if let [x, 0] | [x, 1] = [0, 1] {}
+ if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
+ if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
+ struct TS(u8, u8);
+ if let TS(0, x) | TS(1, x) = TS(0, 0) {}
+ if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
+ if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
+ struct S {
+ x: u8,
+ y: u8,
+ }
+ if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+ if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
new file mode 100644
index 000000000..453c66cbb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
@@ -0,0 +1,179 @@
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:12:12
+ |
+LL | if let box 0 | box 2 = Box::new(0) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnested-or-patterns` implied by `-D warnings`
+help: nest the patterns
+ |
+LL | if let box (0 | 2) = Box::new(0) {}
+ | ~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:13:12
+ |
+LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:15:12
+ |
+LL | if let Some(1) | C0 | Some(2) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let Some(1 | 2) | C0 = None {}
+ | ~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:16:12
+ |
+LL | if let &mut 0 | &mut 2 = &mut 0 {}
+ | ^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let &mut (0 | 2) = &mut 0 {}
+ | ~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:17:12
+ |
+LL | if let x @ 0 | x @ 2 = 0 {}
+ | ^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let x @ (0 | 2) = 0 {}
+ | ~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:18:12
+ |
+LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let (0, 1 | 2 | 3) = (0, 0) {}
+ | ~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:19:12
+ |
+LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let (1 | 2 | 3, 0) = (0, 0) {}
+ | ~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:20:12
+ |
+LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {}
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:21:12
+ |
+LL | if let [0] | [1] = [0] {}
+ | ^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let [0 | 1] = [0] {}
+ | ~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:22:12
+ |
+LL | if let [x, 0] | [x, 1] = [0, 1] {}
+ | ^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let [x, 0 | 1] = [0, 1] {}
+ | ~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:23:12
+ |
+LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let [x, 0 | 1 | 2] = [0, 1] {}
+ | ~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:24:12
+ |
+LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {}
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:26:12
+ |
+LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let TS(0 | 1, x) = TS(0, 0) {}
+ | ~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:27:12
+ |
+LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
+ | ~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:28:12
+ |
+LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns.rs:33:12
+ |
+LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
+ | ~~~~~~~~~~~~~~~~~
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed
new file mode 100644
index 000000000..d3539d798
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![feature(box_patterns)]
+#![warn(clippy::unnested_or_patterns)]
+#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
+#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
+
+fn main() {
+ if let Some(Some(0 | 1)) = None {}
+ if let Some(Some(0 | 1 | 2)) = None {}
+ if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
+ if let Some(Some(0 | 1 | 2)) = None {}
+ if let ((0 | 1 | 2,),) = ((0,),) {}
+ if let 0 | 1 | 2 = 0 {}
+ if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+ if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs
new file mode 100644
index 000000000..9cea5cdea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs
@@ -0,0 +1,17 @@
+// run-rustfix
+
+#![feature(box_patterns)]
+#![warn(clippy::unnested_or_patterns)]
+#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
+#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
+
+fn main() {
+ if let Some(Some(0)) | Some(Some(1)) = None {}
+ if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
+ if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
+ if let Some(Some(0) | Some(1 | 2)) = None {}
+ if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
+ if let 0 | (1 | 2) = 0 {}
+ if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
+ if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
new file mode 100644
index 000000000..41e8d3fc7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr
@@ -0,0 +1,91 @@
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:9:12
+ |
+LL | if let Some(Some(0)) | Some(Some(1)) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnested-or-patterns` implied by `-D warnings`
+help: nest the patterns
+ |
+LL | if let Some(Some(0 | 1)) = None {}
+ | ~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:10:12
+ |
+LL | if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let Some(Some(0 | 1 | 2)) = None {}
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:11:12
+ |
+LL | if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:12:12
+ |
+LL | if let Some(Some(0) | Some(1 | 2)) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let Some(Some(0 | 1 | 2)) = None {}
+ | ~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:13:12
+ |
+LL | if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let ((0 | 1 | 2,),) = ((0,),) {}
+ | ~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:14:12
+ |
+LL | if let 0 | (1 | 2) = 0 {}
+ | ^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let 0 | 1 | 2 = 0 {}
+ | ~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:15:12
+ |
+LL | if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: unnested or-patterns
+ --> $DIR/unnested_or_patterns2.rs:16:12
+ |
+LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: nest the patterns
+ |
+LL | if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
+ | ~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unreadable_literal.fixed b/src/tools/clippy/tests/ui/unreadable_literal.fixed
new file mode 100644
index 000000000..a67363b09
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unreadable_literal.fixed
@@ -0,0 +1,46 @@
+// run-rustfix
+
+#![warn(clippy::unreadable_literal)]
+#![allow(unused_tuple_struct_fields)]
+
+struct Foo(u64);
+
+macro_rules! foo {
+ () => {
+ Foo(123123123123)
+ };
+}
+
+struct Bar(f32);
+
+macro_rules! bar {
+ () => {
+ Bar(100200300400.100200300400500)
+ };
+}
+
+fn main() {
+ let _good = (
+ 0b1011_i64,
+ 0o1_234_u32,
+ 0x0123_4567,
+ 65536,
+ 1_2345_6789,
+ 1234_f32,
+ 1_234.12_f32,
+ 1_234.123_f32,
+ 1.123_4_f32,
+ );
+ let _bad = (0b11_0110_i64, 0x1234_5678_usize, 123_456_f32, 1.234_567_f32);
+ let _good_sci = 1.1234e1;
+ let _bad_sci = 1.123_456e1;
+
+ let _fail1 = 0x00ab_cdef;
+ let _fail2: u32 = 0xBAFE_BAFE;
+ let _fail3 = 0x0abc_deff;
+ let _fail4: i128 = 0x00ab_cabc_abca_bcab_cabc;
+ let _fail5 = 1.100_300_400;
+
+ let _ = foo!();
+ let _ = bar!();
+}
diff --git a/src/tools/clippy/tests/ui/unreadable_literal.rs b/src/tools/clippy/tests/ui/unreadable_literal.rs
new file mode 100644
index 000000000..82f04e7ce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unreadable_literal.rs
@@ -0,0 +1,46 @@
+// run-rustfix
+
+#![warn(clippy::unreadable_literal)]
+#![allow(unused_tuple_struct_fields)]
+
+struct Foo(u64);
+
+macro_rules! foo {
+ () => {
+ Foo(123123123123)
+ };
+}
+
+struct Bar(f32);
+
+macro_rules! bar {
+ () => {
+ Bar(100200300400.100200300400500)
+ };
+}
+
+fn main() {
+ let _good = (
+ 0b1011_i64,
+ 0o1_234_u32,
+ 0x1_234_567,
+ 65536,
+ 1_2345_6789,
+ 1234_f32,
+ 1_234.12_f32,
+ 1_234.123_f32,
+ 1.123_4_f32,
+ );
+ let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32);
+ let _good_sci = 1.1234e1;
+ let _bad_sci = 1.123456e1;
+
+ let _fail1 = 0xabcdef;
+ let _fail2: u32 = 0xBAFEBAFE;
+ let _fail3 = 0xabcdeff;
+ let _fail4: i128 = 0xabcabcabcabcabcabc;
+ let _fail5 = 1.100300400;
+
+ let _ = foo!();
+ let _ = bar!();
+}
diff --git a/src/tools/clippy/tests/ui/unreadable_literal.stderr b/src/tools/clippy/tests/ui/unreadable_literal.stderr
new file mode 100644
index 000000000..b51130c6a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unreadable_literal.stderr
@@ -0,0 +1,72 @@
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/unreadable_literal.rs:26:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:34:17
+ |
+LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32);
+ | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
+ |
+ = note: `-D clippy::unreadable-literal` implied by `-D warnings`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:34:31
+ |
+LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32);
+ | ^^^^^^^^^^^^^^^^ help: consider: `0x1234_5678_usize`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:34:49
+ |
+LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32);
+ | ^^^^^^^^^^ help: consider: `123_456_f32`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:34:61
+ |
+LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32);
+ | ^^^^^^^^^^^^ help: consider: `1.234_567_f32`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:36:20
+ |
+LL | let _bad_sci = 1.123456e1;
+ | ^^^^^^^^^^ help: consider: `1.123_456e1`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:38:18
+ |
+LL | let _fail1 = 0xabcdef;
+ | ^^^^^^^^ help: consider: `0x00ab_cdef`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:39:23
+ |
+LL | let _fail2: u32 = 0xBAFEBAFE;
+ | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:40:18
+ |
+LL | let _fail3 = 0xabcdeff;
+ | ^^^^^^^^^ help: consider: `0x0abc_deff`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:41:24
+ |
+LL | let _fail4: i128 = 0xabcabcabcabcabcabc;
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
+
+error: long literal lacking separators
+ --> $DIR/unreadable_literal.rs:42:18
+ |
+LL | let _fail5 = 1.100300400;
+ | ^^^^^^^^^^^ help: consider: `1.100_300_400`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
new file mode 100644
index 000000000..bafca9191
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
@@ -0,0 +1,70 @@
+#![warn(clippy::unsafe_derive_deserialize)]
+#![allow(unused, clippy::missing_safety_doc)]
+
+extern crate serde;
+
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+pub struct A;
+impl A {
+ pub unsafe fn new(_a: i32, _b: i32) -> Self {
+ Self {}
+ }
+}
+
+#[derive(Deserialize)]
+pub struct B;
+impl B {
+ pub unsafe fn unsafe_method(&self) {}
+}
+
+#[derive(Deserialize)]
+pub struct C;
+impl C {
+ pub fn unsafe_block(&self) {
+ unsafe {}
+ }
+}
+
+#[derive(Deserialize)]
+pub struct D;
+impl D {
+ pub fn inner_unsafe_fn(&self) {
+ unsafe fn inner() {}
+ }
+}
+
+// Does not derive `Deserialize`, should be ignored
+pub struct E;
+impl E {
+ pub unsafe fn new(_a: i32, _b: i32) -> Self {
+ Self {}
+ }
+
+ pub unsafe fn unsafe_method(&self) {}
+
+ pub fn unsafe_block(&self) {
+ unsafe {}
+ }
+
+ pub fn inner_unsafe_fn(&self) {
+ unsafe fn inner() {}
+ }
+}
+
+// Does not have methods using `unsafe`, should be ignored
+#[derive(Deserialize)]
+pub struct F;
+
+// Check that we honor the `allow` attribute on the ADT
+#[allow(clippy::unsafe_derive_deserialize)]
+#[derive(Deserialize)]
+pub struct G;
+impl G {
+ pub fn unsafe_block(&self) {
+ unsafe {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
new file mode 100644
index 000000000..18c4276c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
@@ -0,0 +1,39 @@
+error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
+ --> $DIR/unsafe_derive_deserialize.rs:8:10
+ |
+LL | #[derive(Deserialize)]
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings`
+ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
+ = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
+ --> $DIR/unsafe_derive_deserialize.rs:16:10
+ |
+LL | #[derive(Deserialize)]
+ | ^^^^^^^^^^^
+ |
+ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
+ = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
+ --> $DIR/unsafe_derive_deserialize.rs:22:10
+ |
+LL | #[derive(Deserialize)]
+ | ^^^^^^^^^^^
+ |
+ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
+ = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
+ --> $DIR/unsafe_derive_deserialize.rs:30:10
+ |
+LL | #[derive(Deserialize)]
+ | ^^^^^^^^^^^
+ |
+ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
+ = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
new file mode 100644
index 000000000..cde4e96d6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
@@ -0,0 +1,27 @@
+#![allow(unused_imports)]
+#![allow(dead_code)]
+#![warn(clippy::unsafe_removed_from_name)]
+
+use std::cell::UnsafeCell as TotallySafeCell;
+
+use std::cell::UnsafeCell as TotallySafeCellAgain;
+
+// Shouldn't error
+use std::cell::RefCell as ProbablyNotUnsafe;
+use std::cell::RefCell as RefCellThatCantBeUnsafe;
+use std::cell::UnsafeCell as SuperDangerousUnsafeCell;
+use std::cell::UnsafeCell as Dangerunsafe;
+use std::cell::UnsafeCell as Bombsawayunsafe;
+
+mod mod_with_some_unsafe_things {
+ pub struct Safe;
+ pub struct Unsafe;
+}
+
+use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
+
+// Shouldn't error
+use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime;
+use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr
new file mode 100644
index 000000000..4f871cbe4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr
@@ -0,0 +1,22 @@
+error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCell`
+ --> $DIR/unsafe_removed_from_name.rs:5:1
+ |
+LL | use std::cell::UnsafeCell as TotallySafeCell;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unsafe-removed-from-name` implied by `-D warnings`
+
+error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain`
+ --> $DIR/unsafe_removed_from_name.rs:7:1
+ |
+LL | use std::cell::UnsafeCell as TotallySafeCellAgain;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety`
+ --> $DIR/unsafe_removed_from_name.rs:21:1
+ |
+LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
new file mode 100644
index 000000000..f0c2ba7cc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::unseparated_literal_suffix)]
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// Test for proc-macro attribute
+#[derive(ClippyMiniMacroTest)]
+struct Foo;
+
+macro_rules! lit_from_macro {
+ () => {
+ 42_usize
+ };
+}
+
+fn main() {
+ let _ok1 = 1234_i32;
+ let _ok2 = 1234_isize;
+ let _ok3 = 0x123_isize;
+ let _fail1 = 1234_i32;
+ let _fail2 = 1234_u32;
+ let _fail3 = 1234_isize;
+ let _fail4 = 1234_usize;
+ let _fail5 = 0x123_isize;
+
+ let _okf1 = 1.5_f32;
+ let _okf2 = 1_f32;
+ let _failf1 = 1.5_f32;
+ let _failf2 = 1_f32;
+
+ // Test for macro
+ let _ = lit_from_macro!();
+
+ // Counter example
+ let _ = line!();
+ // Because `assert!` contains `line!()` macro.
+ assert_eq!(4897_u32, 32223);
+}
diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
new file mode 100644
index 000000000..f44880b41
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::unseparated_literal_suffix)]
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// Test for proc-macro attribute
+#[derive(ClippyMiniMacroTest)]
+struct Foo;
+
+macro_rules! lit_from_macro {
+ () => {
+ 42usize
+ };
+}
+
+fn main() {
+ let _ok1 = 1234_i32;
+ let _ok2 = 1234_isize;
+ let _ok3 = 0x123_isize;
+ let _fail1 = 1234i32;
+ let _fail2 = 1234u32;
+ let _fail3 = 1234isize;
+ let _fail4 = 1234usize;
+ let _fail5 = 0x123isize;
+
+ let _okf1 = 1.5_f32;
+ let _okf2 = 1_f32;
+ let _failf1 = 1.5f32;
+ let _failf2 = 1f32;
+
+ // Test for macro
+ let _ = lit_from_macro!();
+
+ // Counter example
+ let _ = line!();
+ // Because `assert!` contains `line!()` macro.
+ assert_eq!(4897u32, 32223);
+}
diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr
new file mode 100644
index 000000000..ab2f75e0c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr
@@ -0,0 +1,63 @@
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:24:18
+ |
+LL | let _fail1 = 1234i32;
+ | ^^^^^^^ help: add an underscore: `1234_i32`
+ |
+ = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:25:18
+ |
+LL | let _fail2 = 1234u32;
+ | ^^^^^^^ help: add an underscore: `1234_u32`
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:26:18
+ |
+LL | let _fail3 = 1234isize;
+ | ^^^^^^^^^ help: add an underscore: `1234_isize`
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:27:18
+ |
+LL | let _fail4 = 1234usize;
+ | ^^^^^^^^^ help: add an underscore: `1234_usize`
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:28:18
+ |
+LL | let _fail5 = 0x123isize;
+ | ^^^^^^^^^^ help: add an underscore: `0x123_isize`
+
+error: float type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:32:19
+ |
+LL | let _failf1 = 1.5f32;
+ | ^^^^^^ help: add an underscore: `1.5_f32`
+
+error: float type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:33:19
+ |
+LL | let _failf2 = 1f32;
+ | ^^^^ help: add an underscore: `1_f32`
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:16:9
+ |
+LL | 42usize
+ | ^^^^^^^ help: add an underscore: `42_usize`
+...
+LL | let _ = lit_from_macro!();
+ | ----------------- in this macro invocation
+ |
+ = note: this error originates in the macro `lit_from_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: integer type suffix should be separated by an underscore
+ --> $DIR/unseparated_prefix_literals.rs:41:16
+ |
+LL | assert_eq!(4897u32, 32223);
+ | ^^^^^^^ help: add an underscore: `4897_u32`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs
new file mode 100644
index 000000000..4ca7f29b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_async.rs
@@ -0,0 +1,48 @@
+#![warn(clippy::unused_async)]
+
+use std::future::Future;
+use std::pin::Pin;
+
+async fn foo() -> i32 {
+ 4
+}
+
+async fn bar() -> i32 {
+ foo().await
+}
+
+struct S;
+
+impl S {
+ async fn unused(&self) -> i32 {
+ 1
+ }
+
+ async fn used(&self) -> i32 {
+ self.unused().await
+ }
+}
+
+trait AsyncTrait {
+ fn trait_method() -> Pin<Box<dyn Future<Output = i32>>>;
+}
+
+macro_rules! async_trait_impl {
+ () => {
+ impl AsyncTrait for S {
+ fn trait_method() -> Pin<Box<dyn Future<Output = i32>>> {
+ async fn unused() -> i32 {
+ 5
+ }
+
+ Box::pin(unused())
+ }
+ }
+ };
+}
+async_trait_impl!();
+
+fn main() {
+ foo();
+ bar();
+}
diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr
new file mode 100644
index 000000000..8b8ad065a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_async.stderr
@@ -0,0 +1,23 @@
+error: unused `async` for function with no await statements
+ --> $DIR/unused_async.rs:6:1
+ |
+LL | / async fn foo() -> i32 {
+LL | | 4
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::unused-async` implied by `-D warnings`
+ = help: consider removing the `async` from this function
+
+error: unused `async` for function with no await statements
+ --> $DIR/unused_async.rs:17:5
+ |
+LL | / async fn unused(&self) -> i32 {
+LL | | 1
+LL | | }
+ | |_____^
+ |
+ = help: consider removing the `async` from this function
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs
new file mode 100644
index 000000000..4b0595581
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_io_amount.rs
@@ -0,0 +1,117 @@
+#![allow(dead_code)]
+#![warn(clippy::unused_io_amount)]
+
+extern crate futures;
+use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
+use std::io::{self, Read};
+
+fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
+ s.write(b"test")?;
+ let mut buf = [0u8; 4];
+ s.read(&mut buf)?;
+ Ok(())
+}
+
+fn unwrap<T: io::Read + io::Write>(s: &mut T) {
+ s.write(b"test").unwrap();
+ let mut buf = [0u8; 4];
+ s.read(&mut buf).unwrap();
+}
+
+fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
+ s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
+ s.write_vectored(&[io::IoSlice::new(&[])])?;
+ Ok(())
+}
+
+fn ok(file: &str) -> Option<()> {
+ let mut reader = std::fs::File::open(file).ok()?;
+ let mut result = [0u8; 0];
+ reader.read(&mut result).ok()?;
+ Some(())
+}
+
+#[allow(clippy::redundant_closure)]
+#[allow(clippy::bind_instead_of_map)]
+fn or_else(file: &str) -> io::Result<()> {
+ let mut reader = std::fs::File::open(file)?;
+ let mut result = [0u8; 0];
+ reader.read(&mut result).or_else(|err| Err(err))?;
+ Ok(())
+}
+
+#[derive(Debug)]
+enum Error {
+ Kind,
+}
+
+fn or(file: &str) -> Result<(), Error> {
+ let mut reader = std::fs::File::open(file).unwrap();
+ let mut result = [0u8; 0];
+ reader.read(&mut result).or(Err(Error::Kind))?;
+ Ok(())
+}
+
+fn combine_or(file: &str) -> Result<(), Error> {
+ let mut reader = std::fs::File::open(file).unwrap();
+ let mut result = [0u8; 0];
+ reader
+ .read(&mut result)
+ .or(Err(Error::Kind))
+ .or(Err(Error::Kind))
+ .expect("error");
+ Ok(())
+}
+
+async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
+ w.write(b"hello world").await.unwrap();
+}
+
+async fn bad_async_read<R: AsyncRead + Unpin>(r: &mut R) {
+ let mut buf = [0u8; 0];
+ r.read(&mut buf[..]).await.unwrap();
+}
+
+async fn io_not_ignored_async_write<W: AsyncWrite + Unpin>(mut w: W) {
+ // Here we're forgetting to await the future, so we should get a
+ // warning about _that_ (or we would, if it were enabled), but we
+ // won't get one about ignoring the return value.
+ w.write(b"hello world");
+}
+
+fn bad_async_write_closure<W: AsyncWrite + Unpin + 'static>(w: W) -> impl futures::Future<Output = io::Result<()>> {
+ let mut w = w;
+ async move {
+ w.write(b"hello world").await?;
+ Ok(())
+ }
+}
+
+async fn async_read_nested_or<R: AsyncRead + Unpin>(r: &mut R, do_it: bool) -> Result<[u8; 1], Error> {
+ let mut buf = [0u8; 1];
+ if do_it {
+ r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
+ }
+ Ok(buf)
+}
+
+use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _};
+
+async fn bad_async_write_tokio<W: TokioAsyncWrite + Unpin>(w: &mut W) {
+ w.write(b"hello world").await.unwrap();
+}
+
+async fn bad_async_read_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) {
+ let mut buf = [0u8; 0];
+ r.read(&mut buf[..]).await.unwrap();
+}
+
+async fn undetected_bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
+ // It would be good to detect this case some day, but the current lint
+ // doesn't handle it. (The documentation says that this lint "detects
+ // only common patterns".)
+ let future = w.write(b"Hello world");
+ future.await.unwrap();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr
new file mode 100644
index 000000000..e5bdd993a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr
@@ -0,0 +1,131 @@
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:9:5
+ |
+LL | s.write(b"test")?;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unused-io-amount` implied by `-D warnings`
+ = help: use `Write::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:11:5
+ |
+LL | s.read(&mut buf)?;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:16:5
+ |
+LL | s.write(b"test").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Write::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:18:5
+ |
+LL | s.read(&mut buf).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:22:5
+ |
+LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:23:5
+ |
+LL | s.write_vectored(&[io::IoSlice::new(&[])])?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:30:5
+ |
+LL | reader.read(&mut result).ok()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:39:5
+ |
+LL | reader.read(&mut result).or_else(|err| Err(err))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:51:5
+ |
+LL | reader.read(&mut result).or(Err(Error::Kind))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:58:5
+ |
+LL | / reader
+LL | | .read(&mut result)
+LL | | .or(Err(Error::Kind))
+LL | | .or(Err(Error::Kind))
+LL | | .expect("error");
+ | |________________________^
+ |
+ = help: use `Read::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:67:5
+ |
+LL | w.write(b"hello world").await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:72:5
+ |
+LL | r.read(&mut buf[..]).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:85:9
+ |
+LL | w.write(b"hello world").await?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:93:9
+ |
+LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+ --> $DIR/unused_io_amount.rs:101:5
+ |
+LL | w.write(b"hello world").await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+ --> $DIR/unused_io_amount.rs:106:5
+ |
+LL | r.read(&mut buf[..]).await.unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_rounding.fixed b/src/tools/clippy/tests/ui/unused_rounding.fixed
new file mode 100644
index 000000000..54f85806a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_rounding.fixed
@@ -0,0 +1,9 @@
+// run-rustfix
+#![warn(clippy::unused_rounding)]
+
+fn main() {
+ let _ = 1f32;
+ let _ = 1.0f64;
+ let _ = 1.00f32;
+ let _ = 2e-54f64.floor();
+}
diff --git a/src/tools/clippy/tests/ui/unused_rounding.rs b/src/tools/clippy/tests/ui/unused_rounding.rs
new file mode 100644
index 000000000..8d007bc4a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_rounding.rs
@@ -0,0 +1,9 @@
+// run-rustfix
+#![warn(clippy::unused_rounding)]
+
+fn main() {
+ let _ = 1f32.ceil();
+ let _ = 1.0f64.floor();
+ let _ = 1.00f32.round();
+ let _ = 2e-54f64.floor();
+}
diff --git a/src/tools/clippy/tests/ui/unused_rounding.stderr b/src/tools/clippy/tests/ui/unused_rounding.stderr
new file mode 100644
index 000000000..6cfb02e04
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_rounding.stderr
@@ -0,0 +1,22 @@
+error: used the `ceil` method with a whole number float
+ --> $DIR/unused_rounding.rs:5:13
+ |
+LL | let _ = 1f32.ceil();
+ | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32`
+ |
+ = note: `-D clippy::unused-rounding` implied by `-D warnings`
+
+error: used the `floor` method with a whole number float
+ --> $DIR/unused_rounding.rs:6:13
+ |
+LL | let _ = 1.0f64.floor();
+ | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64`
+
+error: used the `round` method with a whole number float
+ --> $DIR/unused_rounding.rs:7:13
+ |
+LL | let _ = 1.00f32.round();
+ | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_self.rs b/src/tools/clippy/tests/ui/unused_self.rs
new file mode 100644
index 000000000..92e8e1dba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_self.rs
@@ -0,0 +1,149 @@
+#![warn(clippy::unused_self)]
+#![allow(clippy::boxed_local, clippy::fn_params_excessive_bools)]
+
+mod unused_self {
+ use std::pin::Pin;
+ use std::sync::{Arc, Mutex};
+
+ struct A;
+
+ impl A {
+ fn unused_self_move(self) {}
+ fn unused_self_ref(&self) {}
+ fn unused_self_mut_ref(&mut self) {}
+ fn unused_self_pin_ref(self: Pin<&Self>) {}
+ fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
+ fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
+ fn unused_self_box(self: Box<Self>) {}
+ fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
+ x + y
+ }
+ fn unused_self_class_method(&self) {
+ Self::static_method();
+ }
+
+ fn static_method() {}
+ }
+}
+
+mod unused_self_allow {
+ struct A;
+
+ impl A {
+ // shouldn't trigger
+ #[allow(clippy::unused_self)]
+ fn unused_self_move(self) {}
+ }
+
+ struct B;
+
+ // shouldn't trigger
+ #[allow(clippy::unused_self)]
+ impl B {
+ fn unused_self_move(self) {}
+ }
+
+ struct C;
+
+ #[allow(clippy::unused_self)]
+ impl C {
+ #[warn(clippy::unused_self)]
+ fn some_fn((): ()) {}
+
+ // shouldn't trigger
+ fn unused_self_move(self) {}
+ }
+
+ pub struct D;
+
+ impl D {
+ // shouldn't trigger for public methods
+ pub fn unused_self_move(self) {}
+ }
+}
+
+pub use unused_self_allow::D;
+
+mod used_self {
+ use std::pin::Pin;
+
+ struct A {
+ x: u8,
+ }
+
+ impl A {
+ fn used_self_move(self) -> u8 {
+ self.x
+ }
+ fn used_self_ref(&self) -> u8 {
+ self.x
+ }
+ fn used_self_mut_ref(&mut self) {
+ self.x += 1
+ }
+ fn used_self_pin_ref(self: Pin<&Self>) -> u8 {
+ self.x
+ }
+ fn used_self_box(self: Box<Self>) -> u8 {
+ self.x
+ }
+ fn used_self_with_other_unused_args(&self, x: u8, y: u8) -> u8 {
+ self.x
+ }
+ fn used_in_nested_closure(&self) -> u8 {
+ let mut a = || -> u8 { self.x };
+ a()
+ }
+
+ #[allow(clippy::collapsible_if)]
+ fn used_self_method_nested_conditions(&self, a: bool, b: bool, c: bool, d: bool) {
+ if a {
+ if b {
+ if c {
+ if d {
+ self.used_self_ref();
+ }
+ }
+ }
+ }
+ }
+
+ fn foo(&self) -> u32 {
+ let mut sum = 0u32;
+ for i in 0..self.x {
+ sum += i as u32;
+ }
+ sum
+ }
+
+ fn bar(&mut self, x: u8) -> u32 {
+ let mut y = 0u32;
+ for i in 0..x {
+ y += self.foo()
+ }
+ y
+ }
+ }
+}
+
+mod not_applicable {
+ use std::fmt;
+
+ struct A;
+
+ impl fmt::Debug for A {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "A")
+ }
+ }
+
+ impl A {
+ fn method(x: u8, y: u8) {}
+ }
+
+ trait B {
+ fn method(&self) {}
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unused_self.stderr b/src/tools/clippy/tests/ui/unused_self.stderr
new file mode 100644
index 000000000..0534b40ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_self.stderr
@@ -0,0 +1,75 @@
+error: unused `self` argument
+ --> $DIR/unused_self.rs:11:29
+ |
+LL | fn unused_self_move(self) {}
+ | ^^^^
+ |
+ = note: `-D clippy::unused-self` implied by `-D warnings`
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:12:28
+ |
+LL | fn unused_self_ref(&self) {}
+ | ^^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:13:32
+ |
+LL | fn unused_self_mut_ref(&mut self) {}
+ | ^^^^^^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:14:32
+ |
+LL | fn unused_self_pin_ref(self: Pin<&Self>) {}
+ | ^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:15:36
+ |
+LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
+ | ^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:16:35
+ |
+LL | fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
+ | ^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:17:28
+ |
+LL | fn unused_self_box(self: Box<Self>) {}
+ | ^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:18:40
+ |
+LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
+ | ^^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: unused `self` argument
+ --> $DIR/unused_self.rs:21:37
+ |
+LL | fn unused_self_class_method(&self) {
+ | ^^^^^
+ |
+ = help: consider refactoring to a associated function
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed
new file mode 100644
index 000000000..7bb43cf7a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_unit.fixed
@@ -0,0 +1,89 @@
+// run-rustfix
+
+// The output for humans should just highlight the whole span without showing
+// the suggested replacement, but we also want to test that suggested
+// replacement only removes one set of parentheses, rather than naïvely
+// stripping away any starting or ending parenthesis characters—hence this
+// test of the JSON error format.
+
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+#![deny(clippy::unused_unit)]
+#![allow(dead_code)]
+#![allow(clippy::from_over_into)]
+
+struct Unitter;
+impl Unitter {
+ #[allow(clippy::no_effect)]
+ pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G)
+ where G: Fn() {
+ let _y: &dyn Fn() = &f;
+ (); // this should not lint, as it's not in return type position
+ }
+}
+
+impl Into<()> for Unitter {
+ #[rustfmt::skip]
+ fn into(self) {
+
+ }
+}
+
+trait Trait {
+ fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
+ where
+ G: FnMut(),
+ H: Fn();
+}
+
+impl Trait for Unitter {
+ fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
+ where
+ G: FnMut(),
+ H: Fn() {}
+}
+
+fn return_unit() { }
+
+#[allow(clippy::needless_return)]
+#[allow(clippy::never_loop)]
+#[allow(clippy::unit_cmp)]
+fn main() {
+ let u = Unitter;
+ assert_eq!(u.get_unit(|| {}, return_unit), u.into());
+ return_unit();
+ loop {
+ break;
+ }
+ return;
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4076
+fn foo() {
+ macro_rules! foo {
+ (recv($r:expr) -> $res:pat => $body:expr) => {
+ $body
+ }
+ }
+
+ foo! {
+ recv(rx) -> _x => ()
+ }
+}
+
+#[rustfmt::skip]
+fn test(){}
+
+#[rustfmt::skip]
+fn test2(){}
+
+#[rustfmt::skip]
+fn test3(){}
+
+fn macro_expr() {
+ macro_rules! e {
+ () => (());
+ }
+ e!()
+}
diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs
new file mode 100644
index 000000000..21073fb80
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_unit.rs
@@ -0,0 +1,89 @@
+// run-rustfix
+
+// The output for humans should just highlight the whole span without showing
+// the suggested replacement, but we also want to test that suggested
+// replacement only removes one set of parentheses, rather than naïvely
+// stripping away any starting or ending parenthesis characters—hence this
+// test of the JSON error format.
+
+#![feature(custom_inner_attributes)]
+#![rustfmt::skip]
+
+#![deny(clippy::unused_unit)]
+#![allow(dead_code)]
+#![allow(clippy::from_over_into)]
+
+struct Unitter;
+impl Unitter {
+ #[allow(clippy::no_effect)]
+ pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
+ where G: Fn() -> () {
+ let _y: &dyn Fn() -> () = &f;
+ (); // this should not lint, as it's not in return type position
+ }
+}
+
+impl Into<()> for Unitter {
+ #[rustfmt::skip]
+ fn into(self) -> () {
+ ()
+ }
+}
+
+trait Trait {
+ fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
+ where
+ G: FnMut() -> (),
+ H: Fn() -> ();
+}
+
+impl Trait for Unitter {
+ fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
+ where
+ G: FnMut() -> (),
+ H: Fn() -> () {}
+}
+
+fn return_unit() -> () { () }
+
+#[allow(clippy::needless_return)]
+#[allow(clippy::never_loop)]
+#[allow(clippy::unit_cmp)]
+fn main() {
+ let u = Unitter;
+ assert_eq!(u.get_unit(|| {}, return_unit), u.into());
+ return_unit();
+ loop {
+ break();
+ }
+ return();
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/4076
+fn foo() {
+ macro_rules! foo {
+ (recv($r:expr) -> $res:pat => $body:expr) => {
+ $body
+ }
+ }
+
+ foo! {
+ recv(rx) -> _x => ()
+ }
+}
+
+#[rustfmt::skip]
+fn test()->(){}
+
+#[rustfmt::skip]
+fn test2() ->(){}
+
+#[rustfmt::skip]
+fn test3()-> (){}
+
+fn macro_expr() {
+ macro_rules! e {
+ () => (());
+ }
+ e!()
+}
diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr
new file mode 100644
index 000000000..0d2cb7785
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_unit.stderr
@@ -0,0 +1,122 @@
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:19:58
+ |
+LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
+ | ^^^^^^ help: remove the `-> ()`
+ |
+note: the lint level is defined here
+ --> $DIR/unused_unit.rs:12:9
+ |
+LL | #![deny(clippy::unused_unit)]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:19:28
+ |
+LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:20:18
+ |
+LL | where G: Fn() -> () {
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:21:26
+ |
+LL | let _y: &dyn Fn() -> () = &f;
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:28:18
+ |
+LL | fn into(self) -> () {
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit expression
+ --> $DIR/unused_unit.rs:29:9
+ |
+LL | ()
+ | ^^ help: remove the final `()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:34:29
+ |
+LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:36:19
+ |
+LL | G: FnMut() -> (),
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:37:16
+ |
+LL | H: Fn() -> ();
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:41:29
+ |
+LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:43:19
+ |
+LL | G: FnMut() -> (),
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:44:16
+ |
+LL | H: Fn() -> () {}
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:47:17
+ |
+LL | fn return_unit() -> () { () }
+ | ^^^^^^ help: remove the `-> ()`
+
+error: unneeded unit expression
+ --> $DIR/unused_unit.rs:47:26
+ |
+LL | fn return_unit() -> () { () }
+ | ^^ help: remove the final `()`
+
+error: unneeded `()`
+ --> $DIR/unused_unit.rs:57:14
+ |
+LL | break();
+ | ^^ help: remove the `()`
+
+error: unneeded `()`
+ --> $DIR/unused_unit.rs:59:11
+ |
+LL | return();
+ | ^^ help: remove the `()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:76:10
+ |
+LL | fn test()->(){}
+ | ^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:79:11
+ |
+LL | fn test2() ->(){}
+ | ^^^^^ help: remove the `-> ()`
+
+error: unneeded unit return type
+ --> $DIR/unused_unit.rs:82:11
+ |
+LL | fn test3()-> (){}
+ | ^^^^^ help: remove the `-> ()`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs
new file mode 100644
index 000000000..a4a3cd1d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::unwrap_used)]
+
+fn unwrap_option() {
+ let opt = Some(0);
+ let _ = opt.unwrap();
+}
+
+fn unwrap_result() {
+ let res: Result<u8, ()> = Ok(0);
+ let _ = res.unwrap();
+}
+
+fn main() {
+ unwrap_option();
+ unwrap_result();
+}
diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr
new file mode 100644
index 000000000..4f0858005
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap.stderr
@@ -0,0 +1,19 @@
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap.rs:5:13
+ |
+LL | let _ = opt.unwrap();
+ | ^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unwrap-used` implied by `-D warnings`
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: used `unwrap()` on `a Result` value
+ --> $DIR/unwrap.rs:10:13
+ |
+LL | let _ = res.unwrap();
+ | ^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs
new file mode 100644
index 000000000..2aa842adc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs
@@ -0,0 +1,44 @@
+#![warn(clippy::unwrap_in_result)]
+
+struct A;
+
+impl A {
+ // should not be detected
+ fn good_divisible_by_3(i_str: String) -> Result<bool, String> {
+ // checks whether a string represents a number divisible by 3
+ let i_result = i_str.parse::<i32>();
+ match i_result {
+ Err(_e) => Err("Not a number".to_string()),
+ Ok(i) => {
+ if i % 3 == 0 {
+ return Ok(true);
+ }
+ Err("Number is not divisible by 3".to_string())
+ },
+ }
+ }
+
+ // should be detected
+ fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
+ // checks whether a string represents a number divisible by 3
+ let i = i_str.parse::<i32>().unwrap();
+ if i % 3 == 0 {
+ Ok(true)
+ } else {
+ Err("Number is not divisible by 3".to_string())
+ }
+ }
+
+ fn example_option_expect(i_str: String) -> Option<bool> {
+ let i = i_str.parse::<i32>().expect("not a number");
+ if i % 3 == 0 {
+ return Some(true);
+ }
+ None
+ }
+}
+
+fn main() {
+ A::bad_divisible_by_3("3".to_string());
+ A::good_divisible_by_3("3".to_string());
+}
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr
new file mode 100644
index 000000000..56bc2f2d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr
@@ -0,0 +1,41 @@
+error: used unwrap or expect in a function that returns result or option
+ --> $DIR/unwrap_in_result.rs:22:5
+ |
+LL | / fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
+LL | | // checks whether a string represents a number divisible by 3
+LL | | let i = i_str.parse::<i32>().unwrap();
+LL | | if i % 3 == 0 {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::unwrap-in-result` implied by `-D warnings`
+ = help: unwrap and expect should not be used in a function that returns result or option
+note: potential non-recoverable error(s)
+ --> $DIR/unwrap_in_result.rs:24:17
+ |
+LL | let i = i_str.parse::<i32>().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used unwrap or expect in a function that returns result or option
+ --> $DIR/unwrap_in_result.rs:32:5
+ |
+LL | / fn example_option_expect(i_str: String) -> Option<bool> {
+LL | | let i = i_str.parse::<i32>().expect("not a number");
+LL | | if i % 3 == 0 {
+LL | | return Some(true);
+LL | | }
+LL | | None
+LL | | }
+ | |_____^
+ |
+ = help: unwrap and expect should not be used in a function that returns result or option
+note: potential non-recoverable error(s)
+ --> $DIR/unwrap_in_result.rs:33:17
+ |
+LL | let i = i_str.parse::<i32>().expect("not a number");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs
new file mode 100644
index 000000000..bfb41e439
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_or.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::all)]
+
+fn main() {
+ let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
+}
+
+fn new_lines() {
+ let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
+}
diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr
new file mode 100644
index 000000000..c3a7464fd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_or.stderr
@@ -0,0 +1,16 @@
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/unwrap_or.rs:4:47
+ |
+LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
+ |
+ = note: `-D clippy::or-fun-call` implied by `-D warnings`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/unwrap_or.rs:8:47
+ |
+LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed b/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
new file mode 100644
index 000000000..c2b9bd2c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![warn(clippy::unwrap_or_else_default)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
+fn unwrap_or_else_default() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo
+ }
+
+ // fake default, we should not trigger on this
+ fn default() -> Foo {
+ Foo
+ }
+ }
+
+ struct HasDefaultAndDuplicate;
+
+ impl HasDefaultAndDuplicate {
+ fn default() -> Self {
+ HasDefaultAndDuplicate
+ }
+ }
+
+ impl Default for HasDefaultAndDuplicate {
+ fn default() -> Self {
+ HasDefaultAndDuplicate
+ }
+ }
+
+ enum Enum {
+ A(),
+ }
+
+ fn make<T, V>(_: V) -> T {
+ unimplemented!();
+ }
+
+ let with_enum = Some(Enum::A());
+ with_enum.unwrap_or_else(Enum::A);
+
+ let with_new = Some(vec![1]);
+ with_new.unwrap_or_default();
+
+ let with_err: Result<_, ()> = Ok(vec![1]);
+ with_err.unwrap_or_else(make);
+
+ // should not be changed
+ let with_fake_default = None::<Foo>;
+ with_fake_default.unwrap_or_else(Foo::default);
+
+ // should not be changed
+ let with_fake_default2 = None::<HasDefaultAndDuplicate>;
+ with_fake_default2.unwrap_or_else(<HasDefaultAndDuplicate>::default);
+
+ let with_real_default = None::<HasDefaultAndDuplicate>;
+ with_real_default.unwrap_or_default();
+
+ let with_default_trait = Some(1);
+ with_default_trait.unwrap_or_default();
+
+ let with_default_type = Some(1);
+ with_default_type.unwrap_or_default();
+
+ let with_default_type: Option<Vec<u64>> = None;
+ with_default_type.unwrap_or_default();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unwrap_or_else_default.rs b/src/tools/clippy/tests/ui/unwrap_or_else_default.rs
new file mode 100644
index 000000000..d55664990
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_or_else_default.rs
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![warn(clippy::unwrap_or_else_default)]
+#![allow(dead_code)]
+#![allow(clippy::unnecessary_wraps)]
+
+/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
+fn unwrap_or_else_default() {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo
+ }
+
+ // fake default, we should not trigger on this
+ fn default() -> Foo {
+ Foo
+ }
+ }
+
+ struct HasDefaultAndDuplicate;
+
+ impl HasDefaultAndDuplicate {
+ fn default() -> Self {
+ HasDefaultAndDuplicate
+ }
+ }
+
+ impl Default for HasDefaultAndDuplicate {
+ fn default() -> Self {
+ HasDefaultAndDuplicate
+ }
+ }
+
+ enum Enum {
+ A(),
+ }
+
+ fn make<T, V>(_: V) -> T {
+ unimplemented!();
+ }
+
+ let with_enum = Some(Enum::A());
+ with_enum.unwrap_or_else(Enum::A);
+
+ let with_new = Some(vec![1]);
+ with_new.unwrap_or_else(Vec::new);
+
+ let with_err: Result<_, ()> = Ok(vec![1]);
+ with_err.unwrap_or_else(make);
+
+ // should not be changed
+ let with_fake_default = None::<Foo>;
+ with_fake_default.unwrap_or_else(Foo::default);
+
+ // should not be changed
+ let with_fake_default2 = None::<HasDefaultAndDuplicate>;
+ with_fake_default2.unwrap_or_else(<HasDefaultAndDuplicate>::default);
+
+ let with_real_default = None::<HasDefaultAndDuplicate>;
+ with_real_default.unwrap_or_else(<HasDefaultAndDuplicate as Default>::default);
+
+ let with_default_trait = Some(1);
+ with_default_trait.unwrap_or_else(Default::default);
+
+ let with_default_type = Some(1);
+ with_default_type.unwrap_or_else(u64::default);
+
+ let with_default_type: Option<Vec<u64>> = None;
+ with_default_type.unwrap_or_else(Vec::new);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unwrap_or_else_default.stderr b/src/tools/clippy/tests/ui/unwrap_or_else_default.stderr
new file mode 100644
index 000000000..53e31d85e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unwrap_or_else_default.stderr
@@ -0,0 +1,34 @@
+error: use of `.unwrap_or_else(..)` to construct default value
+ --> $DIR/unwrap_or_else_default.rs:48:5
+ |
+LL | with_new.unwrap_or_else(Vec::new);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_new.unwrap_or_default()`
+ |
+ = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings`
+
+error: use of `.unwrap_or_else(..)` to construct default value
+ --> $DIR/unwrap_or_else_default.rs:62:5
+ |
+LL | with_real_default.unwrap_or_else(<HasDefaultAndDuplicate as Default>::default);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()`
+
+error: use of `.unwrap_or_else(..)` to construct default value
+ --> $DIR/unwrap_or_else_default.rs:65:5
+ |
+LL | with_default_trait.unwrap_or_else(Default::default);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_trait.unwrap_or_default()`
+
+error: use of `.unwrap_or_else(..)` to construct default value
+ --> $DIR/unwrap_or_else_default.rs:68:5
+ |
+LL | with_default_type.unwrap_or_else(u64::default);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
+
+error: use of `.unwrap_or_else(..)` to construct default value
+ --> $DIR/unwrap_or_else_default.rs:71:5
+ |
+LL | with_default_type.unwrap_or_else(Vec::new);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh
new file mode 100755
index 000000000..4391499a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/update-all-references.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echo "Please use 'cargo dev bless' instead."
diff --git a/src/tools/clippy/tests/ui/upper_case_acronyms.rs b/src/tools/clippy/tests/ui/upper_case_acronyms.rs
new file mode 100644
index 000000000..48bb9e54b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/upper_case_acronyms.rs
@@ -0,0 +1,41 @@
+#![warn(clippy::upper_case_acronyms)]
+
+struct HTTPResponse; // not linted by default, but with cfg option
+
+struct CString; // not linted
+
+enum Flags {
+ NS, // not linted
+ CWR,
+ ECE,
+ URG,
+ ACK,
+ PSH,
+ RST,
+ SYN,
+ FIN,
+}
+
+// linted with cfg option, beware that lint suggests `GccllvmSomething` instead of
+// `GccLlvmSomething`
+struct GCCLLVMSomething;
+
+// public items must not be linted
+pub struct NOWARNINGHERE;
+pub struct ALSONoWarningHERE;
+
+// enum variants should not be linted if the num is pub
+pub enum ParseError<T> {
+ YDB(u8),
+ Utf8(std::string::FromUtf8Error),
+ Parse(T, String),
+}
+
+// private, do lint here
+enum ParseErrorPrivate<T> {
+ WASD(u8),
+ Utf8(std::string::FromUtf8Error),
+ Parse(T, String),
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/upper_case_acronyms.stderr b/src/tools/clippy/tests/ui/upper_case_acronyms.stderr
new file mode 100644
index 000000000..250b196a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/upper_case_acronyms.stderr
@@ -0,0 +1,58 @@
+error: name `CWR` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:9:5
+ |
+LL | CWR,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr`
+ |
+ = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
+
+error: name `ECE` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:10:5
+ |
+LL | ECE,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Ece`
+
+error: name `URG` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:11:5
+ |
+LL | URG,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Urg`
+
+error: name `ACK` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:12:5
+ |
+LL | ACK,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ack`
+
+error: name `PSH` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:13:5
+ |
+LL | PSH,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Psh`
+
+error: name `RST` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:14:5
+ |
+LL | RST,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Rst`
+
+error: name `SYN` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:15:5
+ |
+LL | SYN,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Syn`
+
+error: name `FIN` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:16:5
+ |
+LL | FIN,
+ | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin`
+
+error: name `WASD` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:36:5
+ |
+LL | WASD(u8),
+ | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
new file mode 100644
index 000000000..4f80aaecc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -0,0 +1,610 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::use_self)]
+#![allow(dead_code, unreachable_code)]
+#![allow(
+ clippy::should_implement_trait,
+ clippy::upper_case_acronyms,
+ clippy::from_over_into,
+ clippy::self_named_constructors
+)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+fn main() {}
+
+mod use_self {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod better {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod lifetimes {
+ struct Foo<'a> {
+ foo_str: &'a str,
+ }
+
+ impl<'a> Foo<'a> {
+ // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
+ // Foo<'b>`
+ fn foo(s: &str) -> Foo {
+ Foo { foo_str: s }
+ }
+ // cannot replace with `Self`, because that's `Foo<'a>`
+ fn bar() -> Foo<'static> {
+ Foo { foo_str: "foo" }
+ }
+
+ // FIXME: the lint does not handle lifetimed struct
+ // `Self` should be applicable here
+ fn clone(&self) -> Foo<'a> {
+ Foo { foo_str: self.foo_str }
+ }
+ }
+}
+
+mod issue2894 {
+ trait IntoBytes {
+ fn to_bytes(self) -> Vec<u8>;
+ }
+
+ // This should not be linted
+ impl IntoBytes for u8 {
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
+ }
+ }
+}
+
+mod existential {
+ struct Foo;
+
+ impl Foo {
+ fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+
+ fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+ }
+}
+
+mod tuple_structs {
+ pub struct TS(i32);
+
+ impl TS {
+ pub fn ts() -> Self {
+ Self(0)
+ }
+ }
+}
+
+mod macros {
+ macro_rules! use_self_expand {
+ () => {
+ fn new() -> Foo {
+ Foo {}
+ }
+ };
+ }
+
+ struct Foo;
+
+ impl Foo {
+ use_self_expand!(); // Should not lint in local macros
+ }
+
+ #[derive(StructAUseSelf)] // Should not lint in derives
+ struct A;
+}
+
+mod nesting {
+ struct Foo;
+ impl Foo {
+ fn foo() {
+ #[allow(unused_imports)]
+ use self::Foo; // Can't use Self here
+ struct Bar {
+ foo: Foo, // Foo != Self
+ }
+
+ impl Bar {
+ fn bar() -> Self {
+ Self { foo: Foo {} }
+ }
+ }
+
+ // Can't use Self here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ // Should lint here
+ fn baz() -> Self {
+ Self {}
+ }
+ }
+
+ enum Enum {
+ A,
+ B(u64),
+ C { field: bool },
+ }
+ impl Enum {
+ fn method() {
+ #[allow(unused_imports)]
+ use self::Enum::*; // Issue 3425
+ static STATIC: Enum = Enum::A; // Can't use Self as type
+ }
+
+ fn method2() {
+ let _ = Self::B(42);
+ let _ = Self::C { field: true };
+ let _ = Self::A;
+ }
+ }
+}
+
+mod issue3410 {
+
+ struct A;
+ struct B;
+
+ trait Trait<T> {
+ fn a(v: T) -> Self;
+ }
+
+ impl Trait<Vec<A>> for Vec<B> {
+ fn a(_: Vec<A>) -> Self {
+ unimplemented!()
+ }
+ }
+
+ impl<T> Trait<Vec<A>> for Vec<T>
+ where
+ T: Trait<B>,
+ {
+ fn a(v: Vec<A>) -> Self {
+ <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+ }
+ }
+}
+
+#[allow(clippy::no_effect, path_statements)]
+mod rustfix {
+ mod nested {
+ pub struct A;
+ }
+
+ impl nested::A {
+ const A: bool = true;
+
+ fn fun_1() {}
+
+ fn fun_2() {
+ Self::fun_1();
+ Self::A;
+
+ Self {};
+ }
+ }
+}
+
+mod issue3567 {
+ struct TestStruct;
+ impl TestStruct {
+ fn from_something() -> Self {
+ Self {}
+ }
+ }
+
+ trait Test {
+ fn test() -> TestStruct;
+ }
+
+ impl Test for TestStruct {
+ fn test() -> TestStruct {
+ Self::from_something()
+ }
+ }
+}
+
+mod paths_created_by_lowering {
+ use std::ops::Range;
+
+ struct S;
+
+ impl S {
+ const A: usize = 0;
+ const B: usize = 1;
+
+ async fn g() -> Self {
+ Self {}
+ }
+
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[Self::A..Self::B]
+ }
+ }
+
+ trait T {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
+ }
+
+ impl T for Range<u8> {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[0..1]
+ }
+ }
+}
+
+// reused from #1997
+mod generics {
+ struct Foo<T> {
+ value: T,
+ }
+
+ impl<T> Foo<T> {
+ // `Self` is applicable here
+ fn foo(value: T) -> Self {
+ Self { value }
+ }
+
+ // `Cannot` use `Self` as a return type as the generic types are different
+ fn bar(value: i32) -> Foo<i32> {
+ Foo { value }
+ }
+ }
+}
+
+mod issue4140 {
+ pub struct Error<From, To> {
+ _from: From,
+ _too: To,
+ }
+
+ pub trait From<T> {
+ type From;
+ type To;
+
+ fn from(value: T) -> Self;
+ }
+
+ pub trait TryFrom<T>
+ where
+ Self: Sized,
+ {
+ type From;
+ type To;
+
+ fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+ }
+
+ // FIXME: Suggested fix results in infinite recursion.
+ // impl<F, T> TryFrom<F> for T
+ // where
+ // T: From<F>,
+ // {
+ // type From = Self::From;
+ // type To = Self::To;
+
+ // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+ // Ok(From::from(value))
+ // }
+ // }
+
+ impl From<bool> for i64 {
+ type From = bool;
+ type To = Self;
+
+ fn from(value: bool) -> Self {
+ if value { 100 } else { 0 }
+ }
+ }
+}
+
+mod issue2843 {
+ trait Foo {
+ type Bar;
+ }
+
+ impl Foo for usize {
+ type Bar = u8;
+ }
+
+ impl<T: Foo> Foo for Option<T> {
+ type Bar = Option<T::Bar>;
+ }
+}
+
+mod issue3859 {
+ pub struct Foo;
+ pub struct Bar([usize; 3]);
+
+ impl Foo {
+ pub const BAR: usize = 3;
+
+ pub fn foo() {
+ const _X: usize = Foo::BAR;
+ // const _Y: usize = Self::BAR;
+ }
+ }
+}
+
+mod issue4305 {
+ trait Foo: 'static {}
+
+ struct Bar;
+
+ impl Foo for Bar {}
+
+ impl<T: Foo> From<T> for Box<dyn Foo> {
+ fn from(t: T) -> Self {
+ Box::new(t)
+ }
+ }
+}
+
+mod lint_at_item_level {
+ struct Foo;
+
+ #[allow(clippy::use_self)]
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ #[allow(clippy::use_self)]
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod lint_at_impl_item_level {
+ struct Foo;
+
+ impl Foo {
+ #[allow(clippy::use_self)]
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ impl Default for Foo {
+ #[allow(clippy::use_self)]
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod issue4734 {
+ #[repr(C, packed)]
+ pub struct X {
+ pub x: u32,
+ }
+
+ impl From<X> for u32 {
+ fn from(c: X) -> Self {
+ unsafe { core::mem::transmute(c) }
+ }
+ }
+}
+
+mod nested_paths {
+ use std::convert::Into;
+ mod submod {
+ pub struct B;
+ pub struct C;
+
+ impl Into<C> for B {
+ fn into(self) -> C {
+ C {}
+ }
+ }
+ }
+
+ struct A<T> {
+ t: T,
+ }
+
+ impl<T> A<T> {
+ fn new<V: Into<T>>(v: V) -> Self {
+ Self { t: Into::into(v) }
+ }
+ }
+
+ impl A<submod::C> {
+ fn test() -> Self {
+ Self::new::<submod::B>(submod::B {})
+ }
+ }
+}
+
+mod issue6818 {
+ #[derive(serde::Deserialize)]
+ struct A {
+ a: i32,
+ }
+}
+
+mod issue7206 {
+ struct MyStruct<const C: char>;
+ impl From<MyStruct<'a'>> for MyStruct<'b'> {
+ fn from(_s: MyStruct<'a'>) -> Self {
+ Self
+ }
+ }
+
+ // keep linting non-`Const` generic args
+ struct S<'a> {
+ inner: &'a str,
+ }
+
+ struct S2<T> {
+ inner: T,
+ }
+
+ impl<T> S2<T> {
+ fn new() -> Self {
+ unimplemented!();
+ }
+ }
+
+ impl<'a> S2<S<'a>> {
+ fn new_again() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod self_is_ty_param {
+ trait Trait {
+ type Type;
+ type Hi;
+
+ fn test();
+ }
+
+ impl<I> Trait for I
+ where
+ I: Iterator,
+ I::Item: Trait, // changing this to Self would require <Self as Iterator>
+ {
+ type Type = I;
+ type Hi = I::Item;
+
+ fn test() {
+ let _: I::Item;
+ let _: I; // this could lint, but is questionable
+ }
+ }
+}
+
+mod use_self_in_pat {
+ enum Foo {
+ Bar,
+ Baz,
+ }
+
+ impl Foo {
+ fn do_stuff(self) {
+ match self {
+ Self::Bar => unimplemented!(),
+ Self::Baz => unimplemented!(),
+ }
+ match Some(1) {
+ Some(_) => unimplemented!(),
+ None => unimplemented!(),
+ }
+ if let Self::Bar = self {
+ unimplemented!()
+ }
+ }
+ }
+}
+
+mod issue8845 {
+ pub enum Something {
+ Num(u8),
+ TupleNums(u8, u8),
+ StructNums { one: u8, two: u8 },
+ }
+
+ struct Foo(u8);
+
+ struct Bar {
+ x: u8,
+ y: usize,
+ }
+
+ impl Something {
+ fn get_value(&self) -> u8 {
+ match self {
+ Self::Num(n) => *n,
+ Self::TupleNums(n, _m) => *n,
+ Self::StructNums { one, two: _ } => *one,
+ }
+ }
+
+ fn use_crate(&self) -> u8 {
+ match self {
+ Self::Num(n) => *n,
+ Self::TupleNums(n, _m) => *n,
+ Self::StructNums { one, two: _ } => *one,
+ }
+ }
+
+ fn imported_values(&self) -> u8 {
+ use Something::*;
+ match self {
+ Num(n) => *n,
+ TupleNums(n, _m) => *n,
+ StructNums { one, two: _ } => *one,
+ }
+ }
+ }
+
+ impl Foo {
+ fn get_value(&self) -> u8 {
+ let Self(x) = self;
+ *x
+ }
+
+ fn use_crate(&self) -> u8 {
+ let Self(x) = self;
+ *x
+ }
+ }
+
+ impl Bar {
+ fn get_value(&self) -> u8 {
+ let Self { x, .. } = self;
+ *x
+ }
+
+ fn use_crate(&self) -> u8 {
+ let Self { x, .. } = self;
+ *x
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
new file mode 100644
index 000000000..52da72db5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -0,0 +1,610 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::use_self)]
+#![allow(dead_code, unreachable_code)]
+#![allow(
+ clippy::should_implement_trait,
+ clippy::upper_case_acronyms,
+ clippy::from_over_into,
+ clippy::self_named_constructors
+)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+fn main() {}
+
+mod use_self {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ fn test() -> Foo {
+ Foo::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod better {
+ struct Foo;
+
+ impl Foo {
+ fn new() -> Self {
+ Self {}
+ }
+ fn test() -> Self {
+ Self::new()
+ }
+ }
+
+ impl Default for Foo {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+}
+
+mod lifetimes {
+ struct Foo<'a> {
+ foo_str: &'a str,
+ }
+
+ impl<'a> Foo<'a> {
+ // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
+ // Foo<'b>`
+ fn foo(s: &str) -> Foo {
+ Foo { foo_str: s }
+ }
+ // cannot replace with `Self`, because that's `Foo<'a>`
+ fn bar() -> Foo<'static> {
+ Foo { foo_str: "foo" }
+ }
+
+ // FIXME: the lint does not handle lifetimed struct
+ // `Self` should be applicable here
+ fn clone(&self) -> Foo<'a> {
+ Foo { foo_str: self.foo_str }
+ }
+ }
+}
+
+mod issue2894 {
+ trait IntoBytes {
+ fn to_bytes(self) -> Vec<u8>;
+ }
+
+ // This should not be linted
+ impl IntoBytes for u8 {
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
+ }
+ }
+}
+
+mod existential {
+ struct Foo;
+
+ impl Foo {
+ fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ foos.iter()
+ }
+
+ fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
+ foos.iter()
+ }
+ }
+}
+
+mod tuple_structs {
+ pub struct TS(i32);
+
+ impl TS {
+ pub fn ts() -> Self {
+ TS(0)
+ }
+ }
+}
+
+mod macros {
+ macro_rules! use_self_expand {
+ () => {
+ fn new() -> Foo {
+ Foo {}
+ }
+ };
+ }
+
+ struct Foo;
+
+ impl Foo {
+ use_self_expand!(); // Should not lint in local macros
+ }
+
+ #[derive(StructAUseSelf)] // Should not lint in derives
+ struct A;
+}
+
+mod nesting {
+ struct Foo;
+ impl Foo {
+ fn foo() {
+ #[allow(unused_imports)]
+ use self::Foo; // Can't use Self here
+ struct Bar {
+ foo: Foo, // Foo != Self
+ }
+
+ impl Bar {
+ fn bar() -> Bar {
+ Bar { foo: Foo {} }
+ }
+ }
+
+ // Can't use Self here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ // Should lint here
+ fn baz() -> Foo {
+ Foo {}
+ }
+ }
+
+ enum Enum {
+ A,
+ B(u64),
+ C { field: bool },
+ }
+ impl Enum {
+ fn method() {
+ #[allow(unused_imports)]
+ use self::Enum::*; // Issue 3425
+ static STATIC: Enum = Enum::A; // Can't use Self as type
+ }
+
+ fn method2() {
+ let _ = Enum::B(42);
+ let _ = Enum::C { field: true };
+ let _ = Enum::A;
+ }
+ }
+}
+
+mod issue3410 {
+
+ struct A;
+ struct B;
+
+ trait Trait<T> {
+ fn a(v: T) -> Self;
+ }
+
+ impl Trait<Vec<A>> for Vec<B> {
+ fn a(_: Vec<A>) -> Self {
+ unimplemented!()
+ }
+ }
+
+ impl<T> Trait<Vec<A>> for Vec<T>
+ where
+ T: Trait<B>,
+ {
+ fn a(v: Vec<A>) -> Self {
+ <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+ }
+ }
+}
+
+#[allow(clippy::no_effect, path_statements)]
+mod rustfix {
+ mod nested {
+ pub struct A;
+ }
+
+ impl nested::A {
+ const A: bool = true;
+
+ fn fun_1() {}
+
+ fn fun_2() {
+ nested::A::fun_1();
+ nested::A::A;
+
+ nested::A {};
+ }
+ }
+}
+
+mod issue3567 {
+ struct TestStruct;
+ impl TestStruct {
+ fn from_something() -> Self {
+ Self {}
+ }
+ }
+
+ trait Test {
+ fn test() -> TestStruct;
+ }
+
+ impl Test for TestStruct {
+ fn test() -> TestStruct {
+ TestStruct::from_something()
+ }
+ }
+}
+
+mod paths_created_by_lowering {
+ use std::ops::Range;
+
+ struct S;
+
+ impl S {
+ const A: usize = 0;
+ const B: usize = 1;
+
+ async fn g() -> S {
+ S {}
+ }
+
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[S::A..S::B]
+ }
+ }
+
+ trait T {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
+ }
+
+ impl T for Range<u8> {
+ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
+ &p[0..1]
+ }
+ }
+}
+
+// reused from #1997
+mod generics {
+ struct Foo<T> {
+ value: T,
+ }
+
+ impl<T> Foo<T> {
+ // `Self` is applicable here
+ fn foo(value: T) -> Foo<T> {
+ Foo::<T> { value }
+ }
+
+ // `Cannot` use `Self` as a return type as the generic types are different
+ fn bar(value: i32) -> Foo<i32> {
+ Foo { value }
+ }
+ }
+}
+
+mod issue4140 {
+ pub struct Error<From, To> {
+ _from: From,
+ _too: To,
+ }
+
+ pub trait From<T> {
+ type From;
+ type To;
+
+ fn from(value: T) -> Self;
+ }
+
+ pub trait TryFrom<T>
+ where
+ Self: Sized,
+ {
+ type From;
+ type To;
+
+ fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+ }
+
+ // FIXME: Suggested fix results in infinite recursion.
+ // impl<F, T> TryFrom<F> for T
+ // where
+ // T: From<F>,
+ // {
+ // type From = Self::From;
+ // type To = Self::To;
+
+ // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+ // Ok(From::from(value))
+ // }
+ // }
+
+ impl From<bool> for i64 {
+ type From = bool;
+ type To = Self;
+
+ fn from(value: bool) -> Self {
+ if value { 100 } else { 0 }
+ }
+ }
+}
+
+mod issue2843 {
+ trait Foo {
+ type Bar;
+ }
+
+ impl Foo for usize {
+ type Bar = u8;
+ }
+
+ impl<T: Foo> Foo for Option<T> {
+ type Bar = Option<T::Bar>;
+ }
+}
+
+mod issue3859 {
+ pub struct Foo;
+ pub struct Bar([usize; 3]);
+
+ impl Foo {
+ pub const BAR: usize = 3;
+
+ pub fn foo() {
+ const _X: usize = Foo::BAR;
+ // const _Y: usize = Self::BAR;
+ }
+ }
+}
+
+mod issue4305 {
+ trait Foo: 'static {}
+
+ struct Bar;
+
+ impl Foo for Bar {}
+
+ impl<T: Foo> From<T> for Box<dyn Foo> {
+ fn from(t: T) -> Self {
+ Box::new(t)
+ }
+ }
+}
+
+mod lint_at_item_level {
+ struct Foo;
+
+ #[allow(clippy::use_self)]
+ impl Foo {
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ #[allow(clippy::use_self)]
+ impl Default for Foo {
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod lint_at_impl_item_level {
+ struct Foo;
+
+ impl Foo {
+ #[allow(clippy::use_self)]
+ fn new() -> Foo {
+ Foo {}
+ }
+ }
+
+ impl Default for Foo {
+ #[allow(clippy::use_self)]
+ fn default() -> Foo {
+ Foo::new()
+ }
+ }
+}
+
+mod issue4734 {
+ #[repr(C, packed)]
+ pub struct X {
+ pub x: u32,
+ }
+
+ impl From<X> for u32 {
+ fn from(c: X) -> Self {
+ unsafe { core::mem::transmute(c) }
+ }
+ }
+}
+
+mod nested_paths {
+ use std::convert::Into;
+ mod submod {
+ pub struct B;
+ pub struct C;
+
+ impl Into<C> for B {
+ fn into(self) -> C {
+ C {}
+ }
+ }
+ }
+
+ struct A<T> {
+ t: T,
+ }
+
+ impl<T> A<T> {
+ fn new<V: Into<T>>(v: V) -> Self {
+ Self { t: Into::into(v) }
+ }
+ }
+
+ impl A<submod::C> {
+ fn test() -> Self {
+ A::new::<submod::B>(submod::B {})
+ }
+ }
+}
+
+mod issue6818 {
+ #[derive(serde::Deserialize)]
+ struct A {
+ a: i32,
+ }
+}
+
+mod issue7206 {
+ struct MyStruct<const C: char>;
+ impl From<MyStruct<'a'>> for MyStruct<'b'> {
+ fn from(_s: MyStruct<'a'>) -> Self {
+ Self
+ }
+ }
+
+ // keep linting non-`Const` generic args
+ struct S<'a> {
+ inner: &'a str,
+ }
+
+ struct S2<T> {
+ inner: T,
+ }
+
+ impl<T> S2<T> {
+ fn new() -> Self {
+ unimplemented!();
+ }
+ }
+
+ impl<'a> S2<S<'a>> {
+ fn new_again() -> Self {
+ S2::new()
+ }
+ }
+}
+
+mod self_is_ty_param {
+ trait Trait {
+ type Type;
+ type Hi;
+
+ fn test();
+ }
+
+ impl<I> Trait for I
+ where
+ I: Iterator,
+ I::Item: Trait, // changing this to Self would require <Self as Iterator>
+ {
+ type Type = I;
+ type Hi = I::Item;
+
+ fn test() {
+ let _: I::Item;
+ let _: I; // this could lint, but is questionable
+ }
+ }
+}
+
+mod use_self_in_pat {
+ enum Foo {
+ Bar,
+ Baz,
+ }
+
+ impl Foo {
+ fn do_stuff(self) {
+ match self {
+ Foo::Bar => unimplemented!(),
+ Foo::Baz => unimplemented!(),
+ }
+ match Some(1) {
+ Some(_) => unimplemented!(),
+ None => unimplemented!(),
+ }
+ if let Foo::Bar = self {
+ unimplemented!()
+ }
+ }
+ }
+}
+
+mod issue8845 {
+ pub enum Something {
+ Num(u8),
+ TupleNums(u8, u8),
+ StructNums { one: u8, two: u8 },
+ }
+
+ struct Foo(u8);
+
+ struct Bar {
+ x: u8,
+ y: usize,
+ }
+
+ impl Something {
+ fn get_value(&self) -> u8 {
+ match self {
+ Something::Num(n) => *n,
+ Something::TupleNums(n, _m) => *n,
+ Something::StructNums { one, two: _ } => *one,
+ }
+ }
+
+ fn use_crate(&self) -> u8 {
+ match self {
+ crate::issue8845::Something::Num(n) => *n,
+ crate::issue8845::Something::TupleNums(n, _m) => *n,
+ crate::issue8845::Something::StructNums { one, two: _ } => *one,
+ }
+ }
+
+ fn imported_values(&self) -> u8 {
+ use Something::*;
+ match self {
+ Num(n) => *n,
+ TupleNums(n, _m) => *n,
+ StructNums { one, two: _ } => *one,
+ }
+ }
+ }
+
+ impl Foo {
+ fn get_value(&self) -> u8 {
+ let Foo(x) = self;
+ *x
+ }
+
+ fn use_crate(&self) -> u8 {
+ let crate::issue8845::Foo(x) = self;
+ *x
+ }
+ }
+
+ impl Bar {
+ fn get_value(&self) -> u8 {
+ let Bar { x, .. } = self;
+ *x
+ }
+
+ fn use_crate(&self) -> u8 {
+ let crate::issue8845::Bar { x, .. } = self;
+ *x
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr
new file mode 100644
index 000000000..f06bb959b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self.stderr
@@ -0,0 +1,250 @@
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:22:21
+ |
+LL | fn new() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+ = note: `-D clippy::use-self` implied by `-D warnings`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:23:13
+ |
+LL | Foo {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:25:22
+ |
+LL | fn test() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:26:13
+ |
+LL | Foo::new()
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:31:25
+ |
+LL | fn default() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:32:13
+ |
+LL | Foo::new()
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:97:24
+ |
+LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:97:55
+ |
+LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:112:13
+ |
+LL | TS(0)
+ | ^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:147:29
+ |
+LL | fn bar() -> Bar {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:148:21
+ |
+LL | Bar { foo: Foo {} }
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:159:21
+ |
+LL | fn baz() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:160:13
+ |
+LL | Foo {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:177:21
+ |
+LL | let _ = Enum::B(42);
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:178:21
+ |
+LL | let _ = Enum::C { field: true };
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:179:21
+ |
+LL | let _ = Enum::A;
+ | ^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:221:13
+ |
+LL | nested::A::fun_1();
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:222:13
+ |
+LL | nested::A::A;
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:224:13
+ |
+LL | nested::A {};
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:243:13
+ |
+LL | TestStruct::from_something()
+ | ^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:257:25
+ |
+LL | async fn g() -> S {
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:258:13
+ |
+LL | S {}
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:262:16
+ |
+LL | &p[S::A..S::B]
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:262:22
+ |
+LL | &p[S::A..S::B]
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:285:29
+ |
+LL | fn foo(value: T) -> Foo<T> {
+ | ^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:286:13
+ |
+LL | Foo::<T> { value }
+ | ^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:458:13
+ |
+LL | A::new::<submod::B>(submod::B {})
+ | ^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:495:13
+ |
+LL | S2::new()
+ | ^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:532:17
+ |
+LL | Foo::Bar => unimplemented!(),
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:533:17
+ |
+LL | Foo::Baz => unimplemented!(),
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:539:20
+ |
+LL | if let Foo::Bar = self {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:563:17
+ |
+LL | Something::Num(n) => *n,
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:564:17
+ |
+LL | Something::TupleNums(n, _m) => *n,
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:565:17
+ |
+LL | Something::StructNums { one, two: _ } => *one,
+ | ^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:571:17
+ |
+LL | crate::issue8845::Something::Num(n) => *n,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:572:17
+ |
+LL | crate::issue8845::Something::TupleNums(n, _m) => *n,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:573:17
+ |
+LL | crate::issue8845::Something::StructNums { one, two: _ } => *one,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:589:17
+ |
+LL | let Foo(x) = self;
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:594:17
+ |
+LL | let crate::issue8845::Foo(x) = self;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:601:17
+ |
+LL | let Bar { x, .. } = self;
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self.rs:606:17
+ |
+LL | let crate::issue8845::Bar { x, .. } = self;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
+
+error: aborting due to 41 previous errors
+
diff --git a/src/tools/clippy/tests/ui/use_self_trait.fixed b/src/tools/clippy/tests/ui/use_self_trait.fixed
new file mode 100644
index 000000000..9bcd692fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self_trait.fixed
@@ -0,0 +1,115 @@
+// run-rustfix
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+#![allow(clippy::should_implement_trait, clippy::boxed_local)]
+
+use std::ops::Mul;
+
+trait SelfTrait {
+ fn refs(p1: &Self) -> &Self;
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
+ fn mut_refs(p1: &mut Self) -> &mut Self;
+ fn nested(p1: Box<Self>, p2: (&u8, &Self));
+ fn vals(r: Self) -> Self;
+}
+
+#[derive(Default)]
+struct Bad;
+
+impl SelfTrait for Bad {
+ fn refs(p1: &Self) -> &Self {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Self) -> &mut Self {
+ p1
+ }
+
+ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
+
+ fn vals(_: Self) -> Self {
+ Self::default()
+ }
+}
+
+impl Mul for Bad {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self {
+ rhs
+ }
+}
+
+impl Clone for Bad {
+ fn clone(&self) -> Self {
+ // FIXME: applicable here
+ Bad
+ }
+}
+
+#[derive(Default)]
+struct Good;
+
+impl SelfTrait for Good {
+ fn refs(p1: &Self) -> &Self {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Self) -> &mut Self {
+ p1
+ }
+
+ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
+
+ fn vals(_: Self) -> Self {
+ Self::default()
+ }
+}
+
+impl Mul for Good {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self {
+ rhs
+ }
+}
+
+trait NameTrait {
+ fn refs(p1: &u8) -> &u8;
+ fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
+ fn mut_refs(p1: &mut u8) -> &mut u8;
+ fn nested(p1: Box<u8>, p2: (&u8, &u8));
+ fn vals(p1: u8) -> u8;
+}
+
+// Using `Self` instead of the type name is OK
+impl NameTrait for u8 {
+ fn refs(p1: &Self) -> &Self {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Self) -> &mut Self {
+ p1
+ }
+
+ fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
+
+ fn vals(_: Self) -> Self {
+ Self::default()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/use_self_trait.rs b/src/tools/clippy/tests/ui/use_self_trait.rs
new file mode 100644
index 000000000..de305d40f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self_trait.rs
@@ -0,0 +1,115 @@
+// run-rustfix
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+#![allow(clippy::should_implement_trait, clippy::boxed_local)]
+
+use std::ops::Mul;
+
+trait SelfTrait {
+ fn refs(p1: &Self) -> &Self;
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
+ fn mut_refs(p1: &mut Self) -> &mut Self;
+ fn nested(p1: Box<Self>, p2: (&u8, &Self));
+ fn vals(r: Self) -> Self;
+}
+
+#[derive(Default)]
+struct Bad;
+
+impl SelfTrait for Bad {
+ fn refs(p1: &Bad) -> &Bad {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Bad) -> &mut Bad {
+ p1
+ }
+
+ fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
+
+ fn vals(_: Bad) -> Bad {
+ Bad::default()
+ }
+}
+
+impl Mul for Bad {
+ type Output = Bad;
+
+ fn mul(self, rhs: Bad) -> Bad {
+ rhs
+ }
+}
+
+impl Clone for Bad {
+ fn clone(&self) -> Self {
+ // FIXME: applicable here
+ Bad
+ }
+}
+
+#[derive(Default)]
+struct Good;
+
+impl SelfTrait for Good {
+ fn refs(p1: &Self) -> &Self {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Self) -> &mut Self {
+ p1
+ }
+
+ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
+
+ fn vals(_: Self) -> Self {
+ Self::default()
+ }
+}
+
+impl Mul for Good {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self {
+ rhs
+ }
+}
+
+trait NameTrait {
+ fn refs(p1: &u8) -> &u8;
+ fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
+ fn mut_refs(p1: &mut u8) -> &mut u8;
+ fn nested(p1: Box<u8>, p2: (&u8, &u8));
+ fn vals(p1: u8) -> u8;
+}
+
+// Using `Self` instead of the type name is OK
+impl NameTrait for u8 {
+ fn refs(p1: &Self) -> &Self {
+ p1
+ }
+
+ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
+ p1
+ }
+
+ fn mut_refs(p1: &mut Self) -> &mut Self {
+ p1
+ }
+
+ fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
+
+ fn vals(_: Self) -> Self {
+ Self::default()
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/use_self_trait.stderr b/src/tools/clippy/tests/ui/use_self_trait.stderr
new file mode 100644
index 000000000..55af3ff2a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/use_self_trait.stderr
@@ -0,0 +1,88 @@
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:21:18
+ |
+LL | fn refs(p1: &Bad) -> &Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+ = note: `-D clippy::use-self` implied by `-D warnings`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:21:27
+ |
+LL | fn refs(p1: &Bad) -> &Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:25:33
+ |
+LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:25:49
+ |
+LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:29:26
+ |
+LL | fn mut_refs(p1: &mut Bad) -> &mut Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:29:39
+ |
+LL | fn mut_refs(p1: &mut Bad) -> &mut Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:33:24
+ |
+LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:33:42
+ |
+LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:35:16
+ |
+LL | fn vals(_: Bad) -> Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:35:24
+ |
+LL | fn vals(_: Bad) -> Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:36:9
+ |
+LL | Bad::default()
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:41:19
+ |
+LL | type Output = Bad;
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:43:23
+ |
+LL | fn mul(self, rhs: Bad) -> Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+ --> $DIR/use_self_trait.rs:43:31
+ |
+LL | fn mul(self, rhs: Bad) -> Bad {
+ | ^^^ help: use the applicable keyword: `Self`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.rs b/src/tools/clippy/tests/ui/used_underscore_binding.rs
new file mode 100644
index 000000000..d20977d55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/used_underscore_binding.rs
@@ -0,0 +1,124 @@
+// aux-build:proc_macro_derive.rs
+
+#![feature(rustc_private)]
+#![warn(clippy::all)]
+#![allow(clippy::blacklisted_name, clippy::eq_op)]
+#![warn(clippy::used_underscore_binding)]
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// This should not trigger the lint. There's underscore binding inside the external derive that
+// would trigger the `used_underscore_binding` lint.
+#[derive(DeriveSomething)]
+struct Baz;
+
+macro_rules! test_macro {
+ () => {{
+ let _foo = 42;
+ _foo + 1
+ }};
+}
+
+/// Tests that we lint if we use a binding with a single leading underscore
+fn prefix_underscore(_foo: u32) -> u32 {
+ _foo + 1
+}
+
+/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion
+fn in_macro_or_desugar(_foo: u32) {
+ println!("{}", _foo);
+ assert_eq!(_foo, _foo);
+
+ test_macro!() + 1;
+}
+
+// Struct for testing use of fields prefixed with an underscore
+struct StructFieldTest {
+ _underscore_field: u32,
+}
+
+/// Tests that we lint the use of a struct field which is prefixed with an underscore
+fn in_struct_field() {
+ let mut s = StructFieldTest { _underscore_field: 0 };
+ s._underscore_field += 1;
+}
+
+/// Tests that we do not lint if the struct field is used in code created with derive.
+#[derive(Clone, Debug)]
+pub struct UnderscoreInStruct {
+ _foo: u32,
+}
+
+/// Tests that we do not lint if the underscore is not a prefix
+fn non_prefix_underscore(some_foo: u32) -> u32 {
+ some_foo + 1
+}
+
+/// Tests that we do not lint if we do not use the binding (simple case)
+fn unused_underscore_simple(_foo: u32) -> u32 {
+ 1
+}
+
+/// Tests that we do not lint if we do not use the binding (complex case). This checks for
+/// compatibility with the built-in `unused_variables` lint.
+fn unused_underscore_complex(mut _foo: u32) -> u32 {
+ _foo += 1;
+ _foo = 2;
+ 1
+}
+
+/// Test that we do not lint for multiple underscores
+fn multiple_underscores(__foo: u32) -> u32 {
+ __foo + 1
+}
+
+// Non-variable bindings with preceding underscore
+fn _fn_test() {}
+struct _StructTest;
+enum _EnumTest {
+ _Empty,
+ _Value(_StructTest),
+}
+
+/// Tests that we do not lint for non-variable bindings
+fn non_variables() {
+ _fn_test();
+ let _s = _StructTest;
+ let _e = match _EnumTest::_Value(_StructTest) {
+ _EnumTest::_Empty => 0,
+ _EnumTest::_Value(_st) => 1,
+ };
+ let f = _fn_test;
+ f();
+}
+
+// Tests that we do not lint if the binding comes from await desugaring,
+// but we do lint the awaited expression. See issue 5360.
+async fn await_desugaring() {
+ async fn foo() {}
+ fn uses_i(_i: i32) {}
+
+ foo().await;
+ ({
+ let _i = 5;
+ uses_i(_i);
+ foo()
+ })
+ .await
+}
+
+fn main() {
+ let foo = 0u32;
+ // tests of unused_underscore lint
+ let _ = prefix_underscore(foo);
+ in_macro_or_desugar(foo);
+ in_struct_field();
+ // possible false positives
+ let _ = non_prefix_underscore(foo);
+ let _ = unused_underscore_simple(foo);
+ let _ = unused_underscore_complex(foo);
+ let _ = multiple_underscores(foo);
+ non_variables();
+ await_desugaring();
+}
diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr
new file mode 100644
index 000000000..61a9161d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr
@@ -0,0 +1,40 @@
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:25:5
+ |
+LL | _foo + 1
+ | ^^^^
+ |
+ = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:30:20
+ |
+LL | println!("{}", _foo);
+ | ^^^^
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:31:16
+ |
+LL | assert_eq!(_foo, _foo);
+ | ^^^^
+
+error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:31:22
+ |
+LL | assert_eq!(_foo, _foo);
+ | ^^^^
+
+error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:44:5
+ |
+LL | s._underscore_field += 1;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+ --> $DIR/used_underscore_binding.rs:105:16
+ |
+LL | uses_i(_i);
+ | ^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/useful_asref.rs b/src/tools/clippy/tests/ui/useful_asref.rs
new file mode 100644
index 000000000..a9f0170a7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useful_asref.rs
@@ -0,0 +1,13 @@
+#![deny(clippy::useless_asref)]
+
+trait Trait {
+ fn as_ptr(&self);
+}
+
+impl<'a> Trait for &'a [u8] {
+ fn as_ptr(&self) {
+ self.as_ref().as_ptr();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed
new file mode 100644
index 000000000..90cb8945e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_asref.fixed
@@ -0,0 +1,136 @@
+// run-rustfix
+
+#![deny(clippy::useless_asref)]
+#![allow(clippy::explicit_auto_deref)]
+
+use std::fmt::Debug;
+
+struct FakeAsRef;
+
+#[allow(clippy::should_implement_trait)]
+impl FakeAsRef {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+struct MoreRef;
+
+impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
+ fn as_ref(&self) -> &&'a &'b &'c MoreRef {
+ &&&&MoreRef
+ }
+}
+
+fn foo_rstr(x: &str) {
+ println!("{:?}", x);
+}
+fn foo_rslice(x: &[i32]) {
+ println!("{:?}", x);
+}
+fn foo_mrslice(x: &mut [i32]) {
+ println!("{:?}", x);
+}
+fn foo_rrrrmr(_: &&&&MoreRef) {
+ println!("so many refs");
+}
+
+fn not_ok() {
+ let rstr: &str = "hello";
+ let mut mrslice: &mut [i32] = &mut [1, 2, 3];
+
+ {
+ let rslice: &[i32] = &*mrslice;
+ foo_rstr(rstr);
+ foo_rstr(rstr);
+ foo_rslice(rslice);
+ foo_rslice(rslice);
+ }
+ {
+ foo_mrslice(mrslice);
+ foo_mrslice(mrslice);
+ foo_rslice(mrslice);
+ foo_rslice(mrslice);
+ }
+
+ {
+ let rrrrrstr = &&&&rstr;
+ let rrrrrslice = &&&&&*mrslice;
+ foo_rslice(rrrrrslice);
+ foo_rslice(rrrrrslice);
+ foo_rstr(rrrrrstr);
+ foo_rstr(rrrrrstr);
+ }
+ {
+ let mrrrrrslice = &mut &mut &mut &mut mrslice;
+ foo_mrslice(mrrrrrslice);
+ foo_mrslice(mrrrrrslice);
+ foo_rslice(mrrrrrslice);
+ foo_rslice(mrrrrrslice);
+ }
+ #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
+ foo_rrrrmr((&&&&MoreRef));
+
+ generic_not_ok(mrslice);
+ generic_ok(mrslice);
+}
+
+fn ok() {
+ let string = "hello".to_owned();
+ let mut arr = [1, 2, 3];
+ let mut vec = vec![1, 2, 3];
+
+ {
+ foo_rstr(string.as_ref());
+ foo_rslice(arr.as_ref());
+ foo_rslice(vec.as_ref());
+ }
+ {
+ foo_mrslice(arr.as_mut());
+ foo_mrslice(vec.as_mut());
+ }
+
+ {
+ let rrrrstring = &&&&string;
+ let rrrrarr = &&&&arr;
+ let rrrrvec = &&&&vec;
+ foo_rstr(rrrrstring.as_ref());
+ foo_rslice(rrrrarr.as_ref());
+ foo_rslice(rrrrvec.as_ref());
+ }
+ {
+ let mrrrrarr = &mut &mut &mut &mut arr;
+ let mrrrrvec = &mut &mut &mut &mut vec;
+ foo_mrslice(mrrrrarr.as_mut());
+ foo_mrslice(mrrrrvec.as_mut());
+ }
+ FakeAsRef.as_ref();
+ foo_rrrrmr(MoreRef.as_ref());
+
+ generic_not_ok(arr.as_mut());
+ generic_ok(&mut arr);
+}
+
+fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
+ println!("{:?}", t);
+}
+fn foo_rt<T: Debug + ?Sized>(t: &T) {
+ println!("{:?}", t);
+}
+
+fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
+ foo_mrt(mrt);
+ foo_mrt(mrt);
+ foo_rt(mrt);
+ foo_rt(mrt);
+}
+
+fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
+ foo_mrt(mru.as_mut());
+ foo_rt(mru.as_ref());
+}
+
+fn main() {
+ not_ok();
+ ok();
+}
diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs
new file mode 100644
index 000000000..cb9f8ae59
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_asref.rs
@@ -0,0 +1,136 @@
+// run-rustfix
+
+#![deny(clippy::useless_asref)]
+#![allow(clippy::explicit_auto_deref)]
+
+use std::fmt::Debug;
+
+struct FakeAsRef;
+
+#[allow(clippy::should_implement_trait)]
+impl FakeAsRef {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+struct MoreRef;
+
+impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
+ fn as_ref(&self) -> &&'a &'b &'c MoreRef {
+ &&&&MoreRef
+ }
+}
+
+fn foo_rstr(x: &str) {
+ println!("{:?}", x);
+}
+fn foo_rslice(x: &[i32]) {
+ println!("{:?}", x);
+}
+fn foo_mrslice(x: &mut [i32]) {
+ println!("{:?}", x);
+}
+fn foo_rrrrmr(_: &&&&MoreRef) {
+ println!("so many refs");
+}
+
+fn not_ok() {
+ let rstr: &str = "hello";
+ let mut mrslice: &mut [i32] = &mut [1, 2, 3];
+
+ {
+ let rslice: &[i32] = &*mrslice;
+ foo_rstr(rstr.as_ref());
+ foo_rstr(rstr);
+ foo_rslice(rslice.as_ref());
+ foo_rslice(rslice);
+ }
+ {
+ foo_mrslice(mrslice.as_mut());
+ foo_mrslice(mrslice);
+ foo_rslice(mrslice.as_ref());
+ foo_rslice(mrslice);
+ }
+
+ {
+ let rrrrrstr = &&&&rstr;
+ let rrrrrslice = &&&&&*mrslice;
+ foo_rslice(rrrrrslice.as_ref());
+ foo_rslice(rrrrrslice);
+ foo_rstr(rrrrrstr.as_ref());
+ foo_rstr(rrrrrstr);
+ }
+ {
+ let mrrrrrslice = &mut &mut &mut &mut mrslice;
+ foo_mrslice(mrrrrrslice.as_mut());
+ foo_mrslice(mrrrrrslice);
+ foo_rslice(mrrrrrslice.as_ref());
+ foo_rslice(mrrrrrslice);
+ }
+ #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
+ foo_rrrrmr((&&&&MoreRef).as_ref());
+
+ generic_not_ok(mrslice);
+ generic_ok(mrslice);
+}
+
+fn ok() {
+ let string = "hello".to_owned();
+ let mut arr = [1, 2, 3];
+ let mut vec = vec![1, 2, 3];
+
+ {
+ foo_rstr(string.as_ref());
+ foo_rslice(arr.as_ref());
+ foo_rslice(vec.as_ref());
+ }
+ {
+ foo_mrslice(arr.as_mut());
+ foo_mrslice(vec.as_mut());
+ }
+
+ {
+ let rrrrstring = &&&&string;
+ let rrrrarr = &&&&arr;
+ let rrrrvec = &&&&vec;
+ foo_rstr(rrrrstring.as_ref());
+ foo_rslice(rrrrarr.as_ref());
+ foo_rslice(rrrrvec.as_ref());
+ }
+ {
+ let mrrrrarr = &mut &mut &mut &mut arr;
+ let mrrrrvec = &mut &mut &mut &mut vec;
+ foo_mrslice(mrrrrarr.as_mut());
+ foo_mrslice(mrrrrvec.as_mut());
+ }
+ FakeAsRef.as_ref();
+ foo_rrrrmr(MoreRef.as_ref());
+
+ generic_not_ok(arr.as_mut());
+ generic_ok(&mut arr);
+}
+
+fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
+ println!("{:?}", t);
+}
+fn foo_rt<T: Debug + ?Sized>(t: &T) {
+ println!("{:?}", t);
+}
+
+fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
+ foo_mrt(mrt.as_mut());
+ foo_mrt(mrt);
+ foo_rt(mrt.as_ref());
+ foo_rt(mrt);
+}
+
+fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
+ foo_mrt(mru.as_mut());
+ foo_rt(mru.as_ref());
+}
+
+fn main() {
+ not_ok();
+ ok();
+}
diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr
new file mode 100644
index 000000000..b21c67bb3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_asref.stderr
@@ -0,0 +1,74 @@
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:44:18
+ |
+LL | foo_rstr(rstr.as_ref());
+ | ^^^^^^^^^^^^^ help: try this: `rstr`
+ |
+note: the lint level is defined here
+ --> $DIR/useless_asref.rs:3:9
+ |
+LL | #![deny(clippy::useless_asref)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:46:20
+ |
+LL | foo_rslice(rslice.as_ref());
+ | ^^^^^^^^^^^^^^^ help: try this: `rslice`
+
+error: this call to `as_mut` does nothing
+ --> $DIR/useless_asref.rs:50:21
+ |
+LL | foo_mrslice(mrslice.as_mut());
+ | ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:52:20
+ |
+LL | foo_rslice(mrslice.as_ref());
+ | ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:59:20
+ |
+LL | foo_rslice(rrrrrslice.as_ref());
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:61:18
+ |
+LL | foo_rstr(rrrrrstr.as_ref());
+ | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
+
+error: this call to `as_mut` does nothing
+ --> $DIR/useless_asref.rs:66:21
+ |
+LL | foo_mrslice(mrrrrrslice.as_mut());
+ | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:68:20
+ |
+LL | foo_rslice(mrrrrrslice.as_ref());
+ | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:72:16
+ |
+LL | foo_rrrrmr((&&&&MoreRef).as_ref());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
+
+error: this call to `as_mut` does nothing
+ --> $DIR/useless_asref.rs:122:13
+ |
+LL | foo_mrt(mrt.as_mut());
+ | ^^^^^^^^^^^^ help: try this: `mrt`
+
+error: this call to `as_ref` does nothing
+ --> $DIR/useless_asref.rs:124:12
+ |
+LL | foo_rt(mrt.as_ref());
+ | ^^^^^^^^^^^^ help: try this: `mrt`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed
new file mode 100644
index 000000000..c23231a99
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_attribute.fixed
@@ -0,0 +1,75 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::useless_attribute)]
+#![warn(unreachable_pub)]
+#![feature(rustc_private)]
+
+#![allow(dead_code)]
+#![cfg_attr(feature = "cargo-clippy", allow(dead_code))]
+#[rustfmt::skip]
+#[allow(unused_imports)]
+#[allow(unused_extern_crates)]
+#[macro_use]
+extern crate rustc_middle;
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// don't lint on unused_import for `use` items
+#[allow(unused_imports)]
+use std::collections;
+
+// don't lint on unused for `use` items
+#[allow(unused)]
+use std::option;
+
+// don't lint on deprecated for `use` items
+mod foo {
+ #[deprecated]
+ pub struct Bar;
+}
+#[allow(deprecated)]
+pub use foo::Bar;
+
+// This should not trigger the lint. There's lint level definitions inside the external derive
+// that would trigger the useless_attribute lint.
+#[derive(DeriveSomething)]
+struct Baz;
+
+// don't lint on unreachable_pub for `use` items
+mod a {
+ mod b {
+ #[allow(dead_code)]
+ #[allow(unreachable_pub)]
+ pub struct C;
+ }
+
+ #[allow(unreachable_pub)]
+ pub use self::b::C;
+}
+
+// don't lint on clippy::wildcard_imports for `use` items
+#[allow(clippy::wildcard_imports)]
+pub use std::io::prelude::*;
+
+// don't lint on clippy::enum_glob_use for `use` items
+#[allow(clippy::enum_glob_use)]
+pub use std::cmp::Ordering::*;
+
+// don't lint on clippy::redundant_pub_crate
+mod c {
+ #[allow(clippy::redundant_pub_crate)]
+ pub(crate) struct S;
+}
+
+fn test_indented_attr() {
+ #![allow(clippy::almost_swapped)]
+ use std::collections::HashSet;
+
+ let _ = HashSet::<u32>::default();
+}
+
+fn main() {
+ test_indented_attr();
+}
diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs
new file mode 100644
index 000000000..7a7b198ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_attribute.rs
@@ -0,0 +1,75 @@
+// run-rustfix
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::useless_attribute)]
+#![warn(unreachable_pub)]
+#![feature(rustc_private)]
+
+#[allow(dead_code)]
+#[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
+#[rustfmt::skip]
+#[allow(unused_imports)]
+#[allow(unused_extern_crates)]
+#[macro_use]
+extern crate rustc_middle;
+
+#[macro_use]
+extern crate proc_macro_derive;
+
+// don't lint on unused_import for `use` items
+#[allow(unused_imports)]
+use std::collections;
+
+// don't lint on unused for `use` items
+#[allow(unused)]
+use std::option;
+
+// don't lint on deprecated for `use` items
+mod foo {
+ #[deprecated]
+ pub struct Bar;
+}
+#[allow(deprecated)]
+pub use foo::Bar;
+
+// This should not trigger the lint. There's lint level definitions inside the external derive
+// that would trigger the useless_attribute lint.
+#[derive(DeriveSomething)]
+struct Baz;
+
+// don't lint on unreachable_pub for `use` items
+mod a {
+ mod b {
+ #[allow(dead_code)]
+ #[allow(unreachable_pub)]
+ pub struct C;
+ }
+
+ #[allow(unreachable_pub)]
+ pub use self::b::C;
+}
+
+// don't lint on clippy::wildcard_imports for `use` items
+#[allow(clippy::wildcard_imports)]
+pub use std::io::prelude::*;
+
+// don't lint on clippy::enum_glob_use for `use` items
+#[allow(clippy::enum_glob_use)]
+pub use std::cmp::Ordering::*;
+
+// don't lint on clippy::redundant_pub_crate
+mod c {
+ #[allow(clippy::redundant_pub_crate)]
+ pub(crate) struct S;
+}
+
+fn test_indented_attr() {
+ #[allow(clippy::almost_swapped)]
+ use std::collections::HashSet;
+
+ let _ = HashSet::<u32>::default();
+}
+
+fn main() {
+ test_indented_attr();
+}
diff --git a/src/tools/clippy/tests/ui/useless_attribute.stderr b/src/tools/clippy/tests/ui/useless_attribute.stderr
new file mode 100644
index 000000000..255d28763
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_attribute.stderr
@@ -0,0 +1,22 @@
+error: useless lint attribute
+ --> $DIR/useless_attribute.rs:8:1
+ |
+LL | #[allow(dead_code)]
+ | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]`
+ |
+ = note: `-D clippy::useless-attribute` implied by `-D warnings`
+
+error: useless lint attribute
+ --> $DIR/useless_attribute.rs:9:1
+ |
+LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
+
+error: useless lint attribute
+ --> $DIR/useless_attribute.rs:67:5
+ |
+LL | #[allow(clippy::almost_swapped)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed
new file mode 100644
index 000000000..70ff08f36
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_conversion.fixed
@@ -0,0 +1,92 @@
+// run-rustfix
+
+#![deny(clippy::useless_conversion)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn test_generic<T: Copy>(val: T) -> T {
+ let _ = val;
+ val
+}
+
+fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
+ // ok
+ let _: i32 = val.into();
+ let _: U = val.into();
+ let _ = U::from(val);
+}
+
+fn test_questionmark() -> Result<(), ()> {
+ {
+ let _: i32 = 0i32;
+ Ok(Ok(()))
+ }??;
+ Ok(())
+}
+
+fn test_issue_3913() -> Result<(), std::io::Error> {
+ use std::fs;
+ use std::path::Path;
+
+ let path = Path::new(".");
+ for _ in fs::read_dir(path)? {}
+
+ Ok(())
+}
+
+fn test_issue_5833() -> Result<(), ()> {
+ let text = "foo\r\nbar\n\nbaz\n";
+ let lines = text.lines();
+ if Some("ok") == lines.into_iter().next() {}
+
+ Ok(())
+}
+
+fn main() {
+ test_generic(10i32);
+ test_generic2::<i32, i32>(10i32);
+ test_questionmark().unwrap();
+ test_issue_3913().unwrap();
+ test_issue_5833().unwrap();
+
+ let _: String = "foo".into();
+ let _: String = From::from("foo");
+ let _ = String::from("foo");
+ #[allow(clippy::useless_conversion)]
+ {
+ let _: String = "foo".into();
+ let _ = String::from("foo");
+ let _ = "".lines().into_iter();
+ }
+
+ let _: String = "foo".to_string();
+ let _: String = "foo".to_string();
+ let _ = "foo".to_string();
+ let _ = format!("A: {:04}", 123);
+ let _ = "".lines();
+ let _ = vec![1, 2, 3].into_iter();
+ let _: String = format!("Hello {}", "world");
+
+ // keep parentheses around `a + b` for suggestion (see #4750)
+ let a: i32 = 1;
+ let b: i32 = 1;
+ let _ = (a + b) * 3;
+
+ // see #7205
+ let s: Foo<'a'> = Foo;
+ let _: Foo<'b'> = s.into();
+ let s2: Foo<'a'> = Foo;
+ let _: Foo<'a'> = s2;
+ let s3: Foo<'a'> = Foo;
+ let _ = s3;
+ let s4: Foo<'a'> = Foo;
+ let _ = vec![s4, s4, s4].into_iter();
+}
+
+#[derive(Copy, Clone)]
+struct Foo<const C: char>;
+
+impl From<Foo<'a'>> for Foo<'b'> {
+ fn from(_s: Foo<'a'>) -> Self {
+ Foo
+ }
+}
diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs
new file mode 100644
index 000000000..f2444a8f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_conversion.rs
@@ -0,0 +1,92 @@
+// run-rustfix
+
+#![deny(clippy::useless_conversion)]
+#![allow(clippy::unnecessary_wraps)]
+
+fn test_generic<T: Copy>(val: T) -> T {
+ let _ = T::from(val);
+ val.into()
+}
+
+fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
+ // ok
+ let _: i32 = val.into();
+ let _: U = val.into();
+ let _ = U::from(val);
+}
+
+fn test_questionmark() -> Result<(), ()> {
+ {
+ let _: i32 = 0i32.into();
+ Ok(Ok(()))
+ }??;
+ Ok(())
+}
+
+fn test_issue_3913() -> Result<(), std::io::Error> {
+ use std::fs;
+ use std::path::Path;
+
+ let path = Path::new(".");
+ for _ in fs::read_dir(path)? {}
+
+ Ok(())
+}
+
+fn test_issue_5833() -> Result<(), ()> {
+ let text = "foo\r\nbar\n\nbaz\n";
+ let lines = text.lines();
+ if Some("ok") == lines.into_iter().next() {}
+
+ Ok(())
+}
+
+fn main() {
+ test_generic(10i32);
+ test_generic2::<i32, i32>(10i32);
+ test_questionmark().unwrap();
+ test_issue_3913().unwrap();
+ test_issue_5833().unwrap();
+
+ let _: String = "foo".into();
+ let _: String = From::from("foo");
+ let _ = String::from("foo");
+ #[allow(clippy::useless_conversion)]
+ {
+ let _: String = "foo".into();
+ let _ = String::from("foo");
+ let _ = "".lines().into_iter();
+ }
+
+ let _: String = "foo".to_string().into();
+ let _: String = From::from("foo".to_string());
+ let _ = String::from("foo".to_string());
+ let _ = String::from(format!("A: {:04}", 123));
+ let _ = "".lines().into_iter();
+ let _ = vec![1, 2, 3].into_iter().into_iter();
+ let _: String = format!("Hello {}", "world").into();
+
+ // keep parentheses around `a + b` for suggestion (see #4750)
+ let a: i32 = 1;
+ let b: i32 = 1;
+ let _ = i32::from(a + b) * 3;
+
+ // see #7205
+ let s: Foo<'a'> = Foo;
+ let _: Foo<'b'> = s.into();
+ let s2: Foo<'a'> = Foo;
+ let _: Foo<'a'> = s2.into();
+ let s3: Foo<'a'> = Foo;
+ let _ = Foo::<'a'>::from(s3);
+ let s4: Foo<'a'> = Foo;
+ let _ = vec![s4, s4, s4].into_iter().into_iter();
+}
+
+#[derive(Copy, Clone)]
+struct Foo<const C: char>;
+
+impl From<Foo<'a'>> for Foo<'b'> {
+ fn from(_s: Foo<'a'>) -> Self {
+ Foo
+ }
+}
diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr
new file mode 100644
index 000000000..e6760f700
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_conversion.stderr
@@ -0,0 +1,92 @@
+error: useless conversion to the same type: `T`
+ --> $DIR/useless_conversion.rs:7:13
+ |
+LL | let _ = T::from(val);
+ | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val`
+ |
+note: the lint level is defined here
+ --> $DIR/useless_conversion.rs:3:9
+ |
+LL | #![deny(clippy::useless_conversion)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: useless conversion to the same type: `T`
+ --> $DIR/useless_conversion.rs:8:5
+ |
+LL | val.into()
+ | ^^^^^^^^^^ help: consider removing `.into()`: `val`
+
+error: useless conversion to the same type: `i32`
+ --> $DIR/useless_conversion.rs:20:22
+ |
+LL | let _: i32 = 0i32.into();
+ | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion.rs:61:21
+ |
+LL | let _: String = "foo".to_string().into();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion.rs:62:21
+ |
+LL | let _: String = From::from("foo".to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion.rs:63:13
+ |
+LL | let _ = String::from("foo".to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion.rs:64:13
+ |
+LL | let _ = String::from(format!("A: {:04}", 123));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)`
+
+error: useless conversion to the same type: `std::str::Lines`
+ --> $DIR/useless_conversion.rs:65:13
+ |
+LL | let _ = "".lines().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()`
+
+error: useless conversion to the same type: `std::vec::IntoIter<i32>`
+ --> $DIR/useless_conversion.rs:66:13
+ |
+LL | let _ = vec![1, 2, 3].into_iter().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion.rs:67:21
+ |
+LL | let _: String = format!("Hello {}", "world").into();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")`
+
+error: useless conversion to the same type: `i32`
+ --> $DIR/useless_conversion.rs:72:13
+ |
+LL | let _ = i32::from(a + b) * 3;
+ | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)`
+
+error: useless conversion to the same type: `Foo<'a'>`
+ --> $DIR/useless_conversion.rs:78:23
+ |
+LL | let _: Foo<'a'> = s2.into();
+ | ^^^^^^^^^ help: consider removing `.into()`: `s2`
+
+error: useless conversion to the same type: `Foo<'a'>`
+ --> $DIR/useless_conversion.rs:80:13
+ |
+LL | let _ = Foo::<'a'>::from(s3);
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3`
+
+error: useless conversion to the same type: `std::vec::IntoIter<Foo<'a'>>`
+ --> $DIR/useless_conversion.rs:82:13
+ |
+LL | let _ = vec![s4, s4, s4].into_iter().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()`
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs
new file mode 100644
index 000000000..39f54c27b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs
@@ -0,0 +1,40 @@
+#![deny(clippy::useless_conversion)]
+
+fn test_generic<T: Copy>(val: T) -> T {
+ let _ = T::try_from(val).unwrap();
+ val.try_into().unwrap()
+}
+
+fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
+ // ok
+ let _: i32 = val.try_into().unwrap();
+ let _: U = val.try_into().unwrap();
+ let _ = U::try_from(val).unwrap();
+}
+
+fn main() {
+ test_generic(10i32);
+ test_generic2::<i32, i32>(10i32);
+
+ let _: String = "foo".try_into().unwrap();
+ let _: String = TryFrom::try_from("foo").unwrap();
+ let _ = String::try_from("foo").unwrap();
+ #[allow(clippy::useless_conversion)]
+ {
+ let _ = String::try_from("foo").unwrap();
+ let _: String = "foo".try_into().unwrap();
+ }
+ let _: String = "foo".to_string().try_into().unwrap();
+ let _: String = TryFrom::try_from("foo".to_string()).unwrap();
+ let _ = String::try_from("foo".to_string()).unwrap();
+ let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
+ let _: String = format!("Hello {}", "world").try_into().unwrap();
+ let _: String = "".to_owned().try_into().unwrap();
+ let _: String = match String::from("_").try_into() {
+ Ok(a) => a,
+ Err(_) => "".into(),
+ };
+ // FIXME this is a false negative
+ #[allow(clippy::cmp_owned)]
+ if String::from("a") == TryInto::<String>::try_into(String::from("a")).unwrap() {}
+}
diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr
new file mode 100644
index 000000000..b691c13f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr
@@ -0,0 +1,79 @@
+error: useless conversion to the same type: `T`
+ --> $DIR/useless_conversion_try.rs:4:13
+ |
+LL | let _ = T::try_from(val).unwrap();
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/useless_conversion_try.rs:1:9
+ |
+LL | #![deny(clippy::useless_conversion)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider removing `T::try_from()`
+
+error: useless conversion to the same type: `T`
+ --> $DIR/useless_conversion_try.rs:5:5
+ |
+LL | val.try_into().unwrap()
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider removing `.try_into()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:27:21
+ |
+LL | let _: String = "foo".to_string().try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `.try_into()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:28:21
+ |
+LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `TryFrom::try_from()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:29:13
+ |
+LL | let _ = String::try_from("foo".to_string()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `String::try_from()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:30:13
+ |
+LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `String::try_from()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:31:21
+ |
+LL | let _: String = format!("Hello {}", "world").try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `.try_into()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:32:21
+ |
+LL | let _: String = "".to_owned().try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `.try_into()`
+
+error: useless conversion to the same type: `std::string::String`
+ --> $DIR/useless_conversion_try.rs:33:27
+ |
+LL | let _: String = match String::from("_").try_into() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing `.try_into()`
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed
new file mode 100644
index 000000000..318f9c2dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec.fixed
@@ -0,0 +1,78 @@
+// run-rustfix
+#![allow(clippy::nonstandard_macro_braces)]
+#![warn(clippy::useless_vec)]
+
+#[derive(Debug)]
+struct NonCopy;
+
+fn on_slice(_: &[u8]) {}
+
+fn on_mut_slice(_: &mut [u8]) {}
+
+#[allow(clippy::ptr_arg)]
+fn on_vec(_: &Vec<u8>) {}
+
+fn on_mut_vec(_: &mut Vec<u8>) {}
+
+struct Line {
+ length: usize,
+}
+
+impl Line {
+ fn length(&self) -> usize {
+ self.length
+ }
+}
+
+fn main() {
+ on_slice(&[]);
+ on_slice(&[]);
+ on_mut_slice(&mut []);
+
+ on_slice(&[1, 2]);
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut [1, 2]);
+
+ on_slice(&[1, 2]);
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut [1, 2]);
+ #[rustfmt::skip]
+ on_slice(&[1, 2]);
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut [1, 2]);
+
+ on_slice(&[1; 2]);
+ on_slice(&[1; 2]);
+ on_mut_slice(&mut [1; 2]);
+
+ on_vec(&vec![]);
+ on_vec(&vec![1, 2]);
+ on_vec(&vec![1; 2]);
+ on_mut_vec(&mut vec![]);
+ on_mut_vec(&mut vec![1, 2]);
+ on_mut_vec(&mut vec![1; 2]);
+
+ // Now with non-constant expressions
+ let line = Line { length: 2 };
+
+ on_slice(&vec![2; line.length]);
+ on_slice(&vec![2; line.length()]);
+ on_mut_slice(&mut vec![2; line.length]);
+ on_mut_slice(&mut vec![2; line.length()]);
+
+ for a in &[1, 2, 3] {
+ println!("{:?}", a);
+ }
+
+ for a in vec![NonCopy, NonCopy] {
+ println!("{:?}", a);
+ }
+
+ on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+ on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+
+ // Ok
+ for a in vec![1; 201] {
+ println!("{:?}", a);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs
new file mode 100644
index 000000000..d7673ce3e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec.rs
@@ -0,0 +1,78 @@
+// run-rustfix
+#![allow(clippy::nonstandard_macro_braces)]
+#![warn(clippy::useless_vec)]
+
+#[derive(Debug)]
+struct NonCopy;
+
+fn on_slice(_: &[u8]) {}
+
+fn on_mut_slice(_: &mut [u8]) {}
+
+#[allow(clippy::ptr_arg)]
+fn on_vec(_: &Vec<u8>) {}
+
+fn on_mut_vec(_: &mut Vec<u8>) {}
+
+struct Line {
+ length: usize,
+}
+
+impl Line {
+ fn length(&self) -> usize {
+ self.length
+ }
+}
+
+fn main() {
+ on_slice(&vec![]);
+ on_slice(&[]);
+ on_mut_slice(&mut vec![]);
+
+ on_slice(&vec![1, 2]);
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut vec![1, 2]);
+
+ on_slice(&vec![1, 2]);
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut vec![1, 2]);
+ #[rustfmt::skip]
+ on_slice(&vec!(1, 2));
+ on_slice(&[1, 2]);
+ on_mut_slice(&mut vec![1, 2]);
+
+ on_slice(&vec![1; 2]);
+ on_slice(&[1; 2]);
+ on_mut_slice(&mut vec![1; 2]);
+
+ on_vec(&vec![]);
+ on_vec(&vec![1, 2]);
+ on_vec(&vec![1; 2]);
+ on_mut_vec(&mut vec![]);
+ on_mut_vec(&mut vec![1, 2]);
+ on_mut_vec(&mut vec![1; 2]);
+
+ // Now with non-constant expressions
+ let line = Line { length: 2 };
+
+ on_slice(&vec![2; line.length]);
+ on_slice(&vec![2; line.length()]);
+ on_mut_slice(&mut vec![2; line.length]);
+ on_mut_slice(&mut vec![2; line.length()]);
+
+ for a in vec![1, 2, 3] {
+ println!("{:?}", a);
+ }
+
+ for a in vec![NonCopy, NonCopy] {
+ println!("{:?}", a);
+ }
+
+ on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+ on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+
+ // Ok
+ for a in vec![1; 201] {
+ println!("{:?}", a);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr
new file mode 100644
index 000000000..7d1de05a5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec.stderr
@@ -0,0 +1,70 @@
+error: useless use of `vec!`
+ --> $DIR/vec.rs:28:14
+ |
+LL | on_slice(&vec![]);
+ | ^^^^^^^ help: you can use a slice directly: `&[]`
+ |
+ = note: `-D clippy::useless-vec` implied by `-D warnings`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:30:18
+ |
+LL | on_mut_slice(&mut vec![]);
+ | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:32:14
+ |
+LL | on_slice(&vec![1, 2]);
+ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:34:18
+ |
+LL | on_mut_slice(&mut vec![1, 2]);
+ | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:36:14
+ |
+LL | on_slice(&vec![1, 2]);
+ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:38:18
+ |
+LL | on_mut_slice(&mut vec![1, 2]);
+ | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:40:14
+ |
+LL | on_slice(&vec!(1, 2));
+ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:42:18
+ |
+LL | on_mut_slice(&mut vec![1, 2]);
+ | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:44:14
+ |
+LL | on_slice(&vec![1; 2]);
+ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:46:18
+ |
+LL | on_mut_slice(&mut vec![1; 2]);
+ | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]`
+
+error: useless use of `vec!`
+ --> $DIR/vec.rs:63:14
+ |
+LL | for a in vec![1, 2, 3] {
+ | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/vec_box_sized.fixed b/src/tools/clippy/tests/ui/vec_box_sized.fixed
new file mode 100644
index 000000000..a40d91fdb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_box_sized.fixed
@@ -0,0 +1,54 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+struct SizedStruct(i32);
+struct UnsizedStruct([i32]);
+struct BigStruct([i32; 10000]);
+
+/// The following should trigger the lint
+mod should_trigger {
+ use super::SizedStruct;
+ const C: Vec<i32> = Vec::new();
+ static S: Vec<i32> = Vec::new();
+
+ struct StructWithVecBox {
+ sized_type: Vec<SizedStruct>,
+ }
+
+ struct A(Vec<SizedStruct>);
+ struct B(Vec<Vec<u32>>);
+}
+
+/// The following should not trigger the lint
+mod should_not_trigger {
+ use super::{BigStruct, UnsizedStruct};
+
+ struct C(Vec<Box<UnsizedStruct>>);
+ struct D(Vec<Box<BigStruct>>);
+
+ struct StructWithVecBoxButItsUnsized {
+ unsized_type: Vec<Box<UnsizedStruct>>,
+ }
+
+ struct TraitVec<T: ?Sized> {
+ // Regression test for #3720. This was causing an ICE.
+ inner: Vec<Box<T>>,
+ }
+}
+
+mod inner_mod {
+ mod inner {
+ pub struct S;
+ }
+
+ mod inner2 {
+ use super::inner::S;
+
+ pub fn f() -> Vec<S> {
+ vec![]
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/vec_box_sized.rs b/src/tools/clippy/tests/ui/vec_box_sized.rs
new file mode 100644
index 000000000..843bbb64e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_box_sized.rs
@@ -0,0 +1,54 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+struct SizedStruct(i32);
+struct UnsizedStruct([i32]);
+struct BigStruct([i32; 10000]);
+
+/// The following should trigger the lint
+mod should_trigger {
+ use super::SizedStruct;
+ const C: Vec<Box<i32>> = Vec::new();
+ static S: Vec<Box<i32>> = Vec::new();
+
+ struct StructWithVecBox {
+ sized_type: Vec<Box<SizedStruct>>,
+ }
+
+ struct A(Vec<Box<SizedStruct>>);
+ struct B(Vec<Vec<Box<(u32)>>>);
+}
+
+/// The following should not trigger the lint
+mod should_not_trigger {
+ use super::{BigStruct, UnsizedStruct};
+
+ struct C(Vec<Box<UnsizedStruct>>);
+ struct D(Vec<Box<BigStruct>>);
+
+ struct StructWithVecBoxButItsUnsized {
+ unsized_type: Vec<Box<UnsizedStruct>>,
+ }
+
+ struct TraitVec<T: ?Sized> {
+ // Regression test for #3720. This was causing an ICE.
+ inner: Vec<Box<T>>,
+ }
+}
+
+mod inner_mod {
+ mod inner {
+ pub struct S;
+ }
+
+ mod inner2 {
+ use super::inner::S;
+
+ pub fn f() -> Vec<Box<S>> {
+ vec![]
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/vec_box_sized.stderr b/src/tools/clippy/tests/ui/vec_box_sized.stderr
new file mode 100644
index 000000000..c518267f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_box_sized.stderr
@@ -0,0 +1,40 @@
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:12:14
+ |
+LL | const C: Vec<Box<i32>> = Vec::new();
+ | ^^^^^^^^^^^^^ help: try: `Vec<i32>`
+ |
+ = note: `-D clippy::vec-box` implied by `-D warnings`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:13:15
+ |
+LL | static S: Vec<Box<i32>> = Vec::new();
+ | ^^^^^^^^^^^^^ help: try: `Vec<i32>`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:16:21
+ |
+LL | sized_type: Vec<Box<SizedStruct>>,
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:19:14
+ |
+LL | struct A(Vec<Box<SizedStruct>>);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:20:18
+ |
+LL | struct B(Vec<Vec<Box<(u32)>>>);
+ | ^^^^^^^^^^^^^^^ help: try: `Vec<u32>`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/vec_box_sized.rs:48:23
+ |
+LL | pub fn f() -> Vec<Box<S>> {
+ | ^^^^^^^^^^^ help: try: `Vec<S>`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.rs b/src/tools/clippy/tests/ui/vec_init_then_push.rs
new file mode 100644
index 000000000..8dd098a5b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_init_then_push.rs
@@ -0,0 +1,112 @@
+#![allow(unused_variables)]
+#![warn(clippy::vec_init_then_push)]
+
+fn main() {
+ let mut def_err: Vec<u32> = Default::default();
+ def_err.push(0);
+
+ let mut new_err = Vec::<u32>::new();
+ new_err.push(1);
+
+ let mut cap_err = Vec::with_capacity(2);
+ cap_err.push(0);
+ cap_err.push(1);
+ cap_err.push(2);
+ if true {
+ // don't include this one
+ cap_err.push(3);
+ }
+
+ let mut cap_ok = Vec::with_capacity(10);
+ cap_ok.push(0);
+
+ new_err = Vec::new();
+ new_err.push(0);
+
+ let mut vec = Vec::new();
+ // control flow at block final expression
+ if true {
+ // no lint
+ vec.push(1);
+ }
+
+ let mut vec = Vec::with_capacity(5);
+ vec.push(1);
+ vec.push(2);
+ vec.push(3);
+ vec.push(4);
+}
+
+pub fn no_lint() -> Vec<i32> {
+ let mut p = Some(1);
+ let mut vec = Vec::new();
+ loop {
+ match p {
+ None => return vec,
+ Some(i) => {
+ vec.push(i);
+ p = None;
+ },
+ }
+ }
+}
+
+fn _from_iter(items: impl Iterator<Item = u32>) -> Vec<u32> {
+ let mut v = Vec::new();
+ v.push(0);
+ v.push(1);
+ v.extend(items);
+ v
+}
+
+fn _cond_push(x: bool) -> Vec<u32> {
+ let mut v = Vec::new();
+ v.push(0);
+ if x {
+ v.push(1);
+ }
+ v.push(2);
+ v
+}
+
+fn _push_then_edit(x: u32) -> Vec<u32> {
+ let mut v = Vec::new();
+ v.push(x);
+ v.push(1);
+ v[0] = v[1] + 5;
+ v
+}
+
+fn _cond_push_with_large_start(x: bool) -> Vec<u32> {
+ let mut v = Vec::new();
+ v.push(0);
+ v.push(1);
+ v.push(0);
+ v.push(1);
+ v.push(0);
+ v.push(0);
+ v.push(1);
+ v.push(0);
+ if x {
+ v.push(1);
+ }
+
+ let mut v2 = Vec::new();
+ v2.push(0);
+ v2.push(1);
+ v2.push(0);
+ v2.push(1);
+ v2.push(0);
+ v2.push(0);
+ v2.push(1);
+ v2.push(0);
+ v2.extend(&v);
+
+ v2
+}
+
+fn f() {
+ let mut v = Vec::new();
+ v.push((0i32, 0i32));
+ let y = v[0].0.abs();
+}
diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.stderr b/src/tools/clippy/tests/ui/vec_init_then_push.stderr
new file mode 100644
index 000000000..a9da1c520
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_init_then_push.stderr
@@ -0,0 +1,73 @@
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:5:5
+ |
+LL | / let mut def_err: Vec<u32> = Default::default();
+LL | | def_err.push(0);
+ | |____________________^ help: consider using the `vec![]` macro: `let def_err: Vec<u32> = vec![..];`
+ |
+ = note: `-D clippy::vec-init-then-push` implied by `-D warnings`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:8:5
+ |
+LL | / let mut new_err = Vec::<u32>::new();
+LL | | new_err.push(1);
+ | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:11:5
+ |
+LL | / let mut cap_err = Vec::with_capacity(2);
+LL | | cap_err.push(0);
+LL | | cap_err.push(1);
+LL | | cap_err.push(2);
+ | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:23:5
+ |
+LL | / new_err = Vec::new();
+LL | | new_err.push(0);
+ | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:73:5
+ |
+LL | / let mut v = Vec::new();
+LL | | v.push(x);
+LL | | v.push(1);
+ | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:81:5
+ |
+LL | / let mut v = Vec::new();
+LL | | v.push(0);
+LL | | v.push(1);
+LL | | v.push(0);
+... |
+LL | | v.push(1);
+LL | | v.push(0);
+ | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:94:5
+ |
+LL | / let mut v2 = Vec::new();
+LL | | v2.push(0);
+LL | | v2.push(1);
+LL | | v2.push(0);
+... |
+LL | | v2.push(1);
+LL | | v2.push(0);
+ | |_______________^ help: consider using the `vec![]` macro: `let mut v2 = vec![..];`
+
+error: calls to `push` immediately after creation
+ --> $DIR/vec_init_then_push.rs:109:5
+ |
+LL | / let mut v = Vec::new();
+LL | | v.push((0i32, 0i32));
+ | |_________________________^ help: consider using the `vec![]` macro: `let v = vec![..];`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.rs b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs
new file mode 100644
index 000000000..7ed27439e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::vec_resize_to_zero)]
+
+fn main() {
+ // applicable here
+ vec![1, 2, 3, 4, 5].resize(0, 5);
+
+ // not applicable
+ vec![1, 2, 3, 4, 5].resize(2, 5);
+
+ // applicable here, but only implemented for integer literals for now
+ vec!["foo", "bar", "baz"].resize(0, "bar");
+
+ // not applicable
+ vec!["foo", "bar", "baz"].resize(2, "bar")
+}
diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr
new file mode 100644
index 000000000..feb846298
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr
@@ -0,0 +1,13 @@
+error: emptying a vector with `resize`
+ --> $DIR/vec_resize_to_zero.rs:5:5
+ |
+LL | vec![1, 2, 3, 4, 5].resize(0, 5);
+ | ^^^^^^^^^^^^^^^^^^^^------------
+ | |
+ | help: ...or you can empty the vector with: `clear()`
+ |
+ = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
+ = help: the arguments may be inverted...
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.rs b/src/tools/clippy/tests/ui/verbose_file_reads.rs
new file mode 100644
index 000000000..e0065e05a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/verbose_file_reads.rs
@@ -0,0 +1,28 @@
+#![warn(clippy::verbose_file_reads)]
+use std::env::temp_dir;
+use std::fs::File;
+use std::io::Read;
+
+struct Struct;
+// To make sure we only warn on File::{read_to_end, read_to_string} calls
+impl Struct {
+ pub fn read_to_end(&self) {}
+
+ pub fn read_to_string(&self) {}
+}
+
+fn main() -> std::io::Result<()> {
+ let path = "foo.txt";
+ // Lint shouldn't catch this
+ let s = Struct;
+ s.read_to_end();
+ s.read_to_string();
+ // Should catch this
+ let mut f = File::open(&path)?;
+ let mut buffer = Vec::new();
+ f.read_to_end(&mut buffer)?;
+ // ...and this
+ let mut string_buffer = String::new();
+ f.read_to_string(&mut string_buffer)?;
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.stderr b/src/tools/clippy/tests/ui/verbose_file_reads.stderr
new file mode 100644
index 000000000..550b6ab67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/verbose_file_reads.stderr
@@ -0,0 +1,19 @@
+error: use of `File::read_to_end`
+ --> $DIR/verbose_file_reads.rs:23:5
+ |
+LL | f.read_to_end(&mut buffer)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::verbose-file-reads` implied by `-D warnings`
+ = help: consider using `fs::read` instead
+
+error: use of `File::read_to_string`
+ --> $DIR/verbose_file_reads.rs:26:5
+ |
+LL | f.read_to_string(&mut string_buffer)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `fs::read_to_string` instead
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs
new file mode 100644
index 000000000..a9a4a0f5a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs
@@ -0,0 +1,44 @@
+use std::fmt::Debug;
+use std::ptr;
+use std::rc::Rc;
+use std::sync::Arc;
+
+#[warn(clippy::vtable_address_comparisons)]
+#[allow(clippy::borrow_as_ptr)]
+
+fn main() {
+ let a: *const dyn Debug = &1 as &dyn Debug;
+ let b: *const dyn Debug = &1 as &dyn Debug;
+
+ // These should fail:
+ let _ = a == b;
+ let _ = a != b;
+ let _ = a < b;
+ let _ = a <= b;
+ let _ = a > b;
+ let _ = a >= b;
+ ptr::eq(a, b);
+
+ let a = &1 as &dyn Debug;
+ let b = &1 as &dyn Debug;
+ ptr::eq(a, b);
+
+ let a: Rc<dyn Debug> = Rc::new(1);
+ Rc::ptr_eq(&a, &a);
+
+ let a: Arc<dyn Debug> = Arc::new(1);
+ Arc::ptr_eq(&a, &a);
+
+ // These should be fine:
+ let a = &1;
+ ptr::eq(a, a);
+
+ let a = Rc::new(1);
+ Rc::ptr_eq(&a, &a);
+
+ let a = Arc::new(1);
+ Arc::ptr_eq(&a, &a);
+
+ let a: &[u8] = b"";
+ ptr::eq(a, a);
+}
diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
new file mode 100644
index 000000000..2f1be61e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
@@ -0,0 +1,83 @@
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:14:13
+ |
+LL | let _ = a == b;
+ | ^^^^^^
+ |
+ = note: `-D clippy::vtable-address-comparisons` implied by `-D warnings`
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:15:13
+ |
+LL | let _ = a != b;
+ | ^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:16:13
+ |
+LL | let _ = a < b;
+ | ^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:17:13
+ |
+LL | let _ = a <= b;
+ | ^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:18:13
+ |
+LL | let _ = a > b;
+ | ^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:19:13
+ |
+LL | let _ = a >= b;
+ | ^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:20:5
+ |
+LL | ptr::eq(a, b);
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:24:5
+ |
+LL | ptr::eq(a, b);
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:27:5
+ |
+LL | Rc::ptr_eq(&a, &a);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: comparing trait object pointers compares a non-unique vtable address
+ --> $DIR/vtable_address_comparisons.rs:30:5
+ |
+LL | Arc::ptr_eq(&a, &a);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider extracting and comparing data pointers only
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs
new file mode 100644
index 000000000..c42e2a79a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_let_loop.rs
@@ -0,0 +1,145 @@
+#![warn(clippy::while_let_loop)]
+
+fn main() {
+ let y = Some(true);
+ loop {
+ if let Some(_x) = y {
+ let _v = 1;
+ } else {
+ break;
+ }
+ }
+
+ #[allow(clippy::never_loop)]
+ loop {
+ // no error, break is not in else clause
+ if let Some(_x) = y {
+ let _v = 1;
+ }
+ break;
+ }
+
+ loop {
+ match y {
+ Some(_x) => true,
+ None => break,
+ };
+ }
+
+ loop {
+ let x = match y {
+ Some(x) => x,
+ None => break,
+ };
+ let _x = x;
+ let _str = "foo";
+ }
+
+ loop {
+ let x = match y {
+ Some(x) => x,
+ None => break,
+ };
+ {
+ let _a = "bar";
+ };
+ {
+ let _b = "foobar";
+ }
+ }
+
+ loop {
+ // no error, else branch does something other than break
+ match y {
+ Some(_x) => true,
+ _ => {
+ let _z = 1;
+ break;
+ },
+ };
+ }
+
+ while let Some(x) = y {
+ // no error, obviously
+ println!("{}", x);
+ }
+
+ // #675, this used to have a wrong suggestion
+ loop {
+ let (e, l) = match "".split_whitespace().next() {
+ Some(word) => (word.is_empty(), word.len()),
+ None => break,
+ };
+
+ let _ = (e, l);
+ }
+}
+
+fn issue771() {
+ let mut a = 100;
+ let b = Some(true);
+ loop {
+ if a > 10 {
+ break;
+ }
+
+ match b {
+ Some(_) => a = 0,
+ None => break,
+ }
+ }
+}
+
+fn issue1017() {
+ let r: Result<u32, u32> = Ok(42);
+ let mut len = 1337;
+
+ loop {
+ match r {
+ Err(_) => len = 0,
+ Ok(length) => {
+ len = length;
+ break;
+ },
+ }
+ }
+}
+
+#[allow(clippy::never_loop)]
+fn issue1948() {
+ // should not trigger clippy::while_let_loop lint because break passes an expression
+ let a = Some(10);
+ let b = loop {
+ if let Some(c) = a {
+ break Some(c);
+ } else {
+ break None;
+ }
+ };
+}
+
+fn issue_7913(m: &std::sync::Mutex<Vec<u32>>) {
+ // Don't lint. The lock shouldn't be held while printing.
+ loop {
+ let x = if let Some(x) = m.lock().unwrap().pop() {
+ x
+ } else {
+ break;
+ };
+
+ println!("{}", x);
+ }
+}
+
+fn issue_5715(mut m: core::cell::RefCell<Option<u32>>) {
+ // Don't lint. The temporary from `borrow_mut` must be dropped before overwriting the `RefCell`.
+ loop {
+ let x = if let &mut Some(x) = &mut *m.borrow_mut() {
+ x
+ } else {
+ break;
+ };
+
+ m = core::cell::RefCell::new(Some(x + 1));
+ }
+}
diff --git a/src/tools/clippy/tests/ui/while_let_loop.stderr b/src/tools/clippy/tests/ui/while_let_loop.stderr
new file mode 100644
index 000000000..13dd0ee22
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_let_loop.stderr
@@ -0,0 +1,63 @@
+error: this loop could be written as a `while let` loop
+ --> $DIR/while_let_loop.rs:5:5
+ |
+LL | / loop {
+LL | | if let Some(_x) = y {
+LL | | let _v = 1;
+LL | | } else {
+LL | | break;
+LL | | }
+LL | | }
+ | |_____^ help: try: `while let Some(_x) = y { .. }`
+ |
+ = note: `-D clippy::while-let-loop` implied by `-D warnings`
+
+error: this loop could be written as a `while let` loop
+ --> $DIR/while_let_loop.rs:22:5
+ |
+LL | / loop {
+LL | | match y {
+LL | | Some(_x) => true,
+LL | | None => break,
+LL | | };
+LL | | }
+ | |_____^ help: try: `while let Some(_x) = y { .. }`
+
+error: this loop could be written as a `while let` loop
+ --> $DIR/while_let_loop.rs:29:5
+ |
+LL | / loop {
+LL | | let x = match y {
+LL | | Some(x) => x,
+LL | | None => break,
+... |
+LL | | let _str = "foo";
+LL | | }
+ | |_____^ help: try: `while let Some(x) = y { .. }`
+
+error: this loop could be written as a `while let` loop
+ --> $DIR/while_let_loop.rs:38:5
+ |
+LL | / loop {
+LL | | let x = match y {
+LL | | Some(x) => x,
+LL | | None => break,
+... |
+LL | | }
+LL | | }
+ | |_____^ help: try: `while let Some(x) = y { .. }`
+
+error: this loop could be written as a `while let` loop
+ --> $DIR/while_let_loop.rs:68:5
+ |
+LL | / loop {
+LL | | let (e, l) = match "".split_whitespace().next() {
+LL | | Some(word) => (word.is_empty(), word.len()),
+LL | | None => break,
+... |
+LL | | let _ = (e, l);
+LL | | }
+ | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
new file mode 100644
index 000000000..c57c46736
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
@@ -0,0 +1,453 @@
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
+#![allow(
+ clippy::never_loop,
+ unreachable_code,
+ unused_mut,
+ dead_code,
+ clippy::equatable_if_let,
+ clippy::manual_find,
+ clippy::redundant_closure_call
+)]
+
+fn base() {
+ let mut iter = 1..20;
+ for x in iter {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ for x in iter {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ for _ in iter {}
+
+ let mut iter = 1..20;
+ while let None = iter.next() {} // this is fine (if nonsensical)
+
+ let mut iter = 1..20;
+ if let Some(x) = iter.next() {
+ // also fine
+ println!("{}", x)
+ }
+
+ // the following shouldn't warn because it can't be written with a for loop
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next())
+ }
+
+ // neither can this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next());
+ }
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ iter = 1..20;
+ }
+}
+
+// Issue #1188
+fn refutable() {
+ let a = [42, 1337];
+ let mut b = a.iter();
+
+ // consume all the 42s
+ while let Some(&42) = b.next() {}
+
+ let a = [(1, 2, 3)];
+ let mut b = a.iter();
+
+ while let Some(&(1, 2, 3)) = b.next() {}
+
+ let a = [Some(42)];
+ let mut b = a.iter();
+
+ while let Some(&None) = b.next() {}
+
+ /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
+ for &42 in b {}
+ for &(1, 2, 3) in b {}
+ for &Option::None in b.next() {}
+ // */
+}
+
+fn refutable2() {
+ // Issue 3780
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.windows(2);
+ while let Some([x, y]) = it.next() {
+ println!("x: {}", x);
+ println!("y: {}", y);
+ }
+
+ let mut it = v.windows(2);
+ while let Some([x, ..]) = it.next() {
+ println!("x: {}", x);
+ }
+
+ let mut it = v.windows(2);
+ while let Some([.., y]) = it.next() {
+ println!("y: {}", y);
+ }
+
+ let mut it = v.windows(2);
+ for [..] in it {}
+
+ let v = vec![[1], [2], [3]];
+ let mut it = v.iter();
+ while let Some([1]) = it.next() {}
+
+ let mut it = v.iter();
+ for [_x] in it {}
+ }
+
+ // binding
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.iter();
+ while let Some(x @ 1) = it.next() {
+ println!("{}", x);
+ }
+
+ let v = vec![[1], [2], [3]];
+ let mut it = v.iter();
+ for x @ [_] in it {
+ println!("{:?}", x);
+ }
+ }
+
+ // false negative
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.iter().map(Some);
+ while let Some(Some(_) | None) = it.next() {
+ println!("1");
+ }
+ }
+}
+
+fn nested_loops() {
+ let a = [42, 1337];
+
+ loop {
+ let mut y = a.iter();
+ for _ in y {
+ // use a for loop here
+ }
+ }
+}
+
+fn issue1121() {
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(&value) = values.iter().next() {
+ values.remove(&value);
+ }
+}
+
+fn issue2965() {
+ // This should not cause an ICE
+
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {}
+}
+
+fn issue3670() {
+ let array = [Some(0), None, Some(1)];
+ let mut iter = array.iter();
+
+ while let Some(elem) = iter.next() {
+ let _ = elem.or_else(|| *iter.next()?);
+ }
+}
+
+fn issue1654() {
+ // should not lint if the iterator is generated on every iteration
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {
+ values.remove(&1);
+ }
+
+ while let Some(..) = values.iter().map(|x| x + 1).next() {}
+
+ let chars = "Hello, World!".char_indices();
+ while let Some((i, ch)) = chars.clone().next() {
+ println!("{}: {}", i, ch);
+ }
+}
+
+fn issue6491() {
+ // Used in outer loop, needs &mut
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ for m in it.by_ref() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+
+ // This is fine, inner loop uses a new iterator.
+ let mut it = 1..40;
+ for n in it {
+ let mut it = 1..40;
+ for m in it {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Weird binding shouldn't change anything.
+ let (mut it, _) = (1..40, 0);
+ for m in it {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Used after the loop, needs &mut.
+ let mut it = 1..40;
+ for m in it.by_ref() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("next item {}", it.next().unwrap());
+
+ println!("n still is {}", n);
+ }
+}
+
+fn issue6231() {
+ // Closure in the outer loop, needs &mut
+ let mut it = 1..40;
+ let mut opt = Some(0);
+ while let Some(n) = opt.take().or_else(|| it.next()) {
+ for m in it.by_ref() {
+ if n % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+}
+
+fn issue1924() {
+ struct S<T>(T);
+ impl<T: Iterator<Item = u32>> S<T> {
+ fn f(&mut self) -> Option<u32> {
+ // Used as a field.
+ for i in self.0.by_ref() {
+ if !(3..8).contains(&i) {
+ return Some(i);
+ }
+ }
+ None
+ }
+
+ fn f2(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.next() {
+ if i == 1 {
+ return self.f();
+ }
+ }
+ None
+ }
+ }
+ impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
+ fn f3(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.0.f();
+ }
+ }
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.f3();
+ }
+ }
+ // This one is fine, a different field is borrowed
+ for i in self.0.0.0.by_ref() {
+ if i == 1 {
+ return self.0.1.take();
+ } else {
+ self.0.1 = Some(i);
+ }
+ }
+ None
+ }
+ }
+
+ struct S2<T>(T, u32);
+ impl<T: Iterator<Item = u32>> Iterator for S2<T> {
+ type Item = u32;
+ fn next(&mut self) -> Option<u32> {
+ self.0.next()
+ }
+ }
+
+ // Don't lint, field of the iterator is accessed in the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == it.1 {
+ break;
+ }
+ }
+
+ // Needs &mut, field of the iterator is accessed after the loop
+ let mut it = S2(1..40, 0);
+ for n in it.by_ref() {
+ if n == 0 {
+ break;
+ }
+ }
+ println!("iterator field {}", it.1);
+}
+
+fn issue7249() {
+ let mut it = 0..10;
+ let mut x = || {
+ // Needs &mut, the closure can be called multiple times
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ };
+ x();
+ x();
+}
+
+fn issue7510() {
+ let mut it = 0..10;
+ let it = &mut it;
+ // Needs to reborrow `it` as the binding isn't mutable
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.next().unwrap());
+
+ struct S<T>(T);
+ let mut it = 0..10;
+ let it = S(&mut it);
+ // Needs to reborrow `it.0` as the binding isn't mutable
+ for x in it.0.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+ struct S<T>(T);
+ let mut s = S(0..10);
+ // Don't lint. `s.0` is used inside the loop.
+ while let Some(_) = s.0.next() {
+ let _ = &mut s.0;
+ }
+}
+
+fn custom_deref() {
+ struct S1<T> {
+ x: T,
+ }
+ struct S2<T>(S1<T>);
+ impl<T> core::ops::Deref for S2<T> {
+ type Target = S1<T>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<T> core::ops::DerefMut for S2<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ let mut s = S2(S1 { x: 0..10 });
+ for x in s.x.by_ref() {
+ println!("{}", x);
+ }
+}
+
+fn issue_8113() {
+ let mut x = [0..10];
+ for x in x[0].by_ref() {
+ println!("{}", x);
+ }
+}
+
+fn fn_once_closure() {
+ let mut it = 0..10;
+ (|| {
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })();
+
+ fn f(_: impl FnOnce()) {}
+ let mut it = 0..10;
+ f(|| {
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f2(_: impl FnMut()) {}
+ let mut it = 0..10;
+ f2(|| {
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f3(_: fn()) {}
+ f3(|| {
+ let mut it = 0..10;
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })
+}
+
+fn main() {
+ let mut it = 0..20;
+ for _ in it {
+ println!("test");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
new file mode 100644
index 000000000..8b9a2dbcc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
@@ -0,0 +1,453 @@
+// run-rustfix
+
+#![warn(clippy::while_let_on_iterator)]
+#![allow(
+ clippy::never_loop,
+ unreachable_code,
+ unused_mut,
+ dead_code,
+ clippy::equatable_if_let,
+ clippy::manual_find,
+ clippy::redundant_closure_call
+)]
+
+fn base() {
+ let mut iter = 1..20;
+ while let Option::Some(x) = iter.next() {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ while let Some(x) = iter.next() {
+ println!("{}", x);
+ }
+
+ let mut iter = 1..20;
+ while let Some(_) = iter.next() {}
+
+ let mut iter = 1..20;
+ while let None = iter.next() {} // this is fine (if nonsensical)
+
+ let mut iter = 1..20;
+ if let Some(x) = iter.next() {
+ // also fine
+ println!("{}", x)
+ }
+
+ // the following shouldn't warn because it can't be written with a for loop
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next())
+ }
+
+ // neither can this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ println!("next: {:?}", iter.next());
+ }
+
+ // or this
+ let mut iter = 1u32..20;
+ while let Some(_) = iter.next() {
+ iter = 1..20;
+ }
+}
+
+// Issue #1188
+fn refutable() {
+ let a = [42, 1337];
+ let mut b = a.iter();
+
+ // consume all the 42s
+ while let Some(&42) = b.next() {}
+
+ let a = [(1, 2, 3)];
+ let mut b = a.iter();
+
+ while let Some(&(1, 2, 3)) = b.next() {}
+
+ let a = [Some(42)];
+ let mut b = a.iter();
+
+ while let Some(&None) = b.next() {}
+
+ /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
+ for &42 in b {}
+ for &(1, 2, 3) in b {}
+ for &Option::None in b.next() {}
+ // */
+}
+
+fn refutable2() {
+ // Issue 3780
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.windows(2);
+ while let Some([x, y]) = it.next() {
+ println!("x: {}", x);
+ println!("y: {}", y);
+ }
+
+ let mut it = v.windows(2);
+ while let Some([x, ..]) = it.next() {
+ println!("x: {}", x);
+ }
+
+ let mut it = v.windows(2);
+ while let Some([.., y]) = it.next() {
+ println!("y: {}", y);
+ }
+
+ let mut it = v.windows(2);
+ while let Some([..]) = it.next() {}
+
+ let v = vec![[1], [2], [3]];
+ let mut it = v.iter();
+ while let Some([1]) = it.next() {}
+
+ let mut it = v.iter();
+ while let Some([_x]) = it.next() {}
+ }
+
+ // binding
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.iter();
+ while let Some(x @ 1) = it.next() {
+ println!("{}", x);
+ }
+
+ let v = vec![[1], [2], [3]];
+ let mut it = v.iter();
+ while let Some(x @ [_]) = it.next() {
+ println!("{:?}", x);
+ }
+ }
+
+ // false negative
+ {
+ let v = vec![1, 2, 3];
+ let mut it = v.iter().map(Some);
+ while let Some(Some(_) | None) = it.next() {
+ println!("1");
+ }
+ }
+}
+
+fn nested_loops() {
+ let a = [42, 1337];
+
+ loop {
+ let mut y = a.iter();
+ while let Some(_) = y.next() {
+ // use a for loop here
+ }
+ }
+}
+
+fn issue1121() {
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(&value) = values.iter().next() {
+ values.remove(&value);
+ }
+}
+
+fn issue2965() {
+ // This should not cause an ICE
+
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {}
+}
+
+fn issue3670() {
+ let array = [Some(0), None, Some(1)];
+ let mut iter = array.iter();
+
+ while let Some(elem) = iter.next() {
+ let _ = elem.or_else(|| *iter.next()?);
+ }
+}
+
+fn issue1654() {
+ // should not lint if the iterator is generated on every iteration
+ use std::collections::HashSet;
+ let mut values = HashSet::new();
+ values.insert(1);
+
+ while let Some(..) = values.iter().next() {
+ values.remove(&1);
+ }
+
+ while let Some(..) = values.iter().map(|x| x + 1).next() {}
+
+ let chars = "Hello, World!".char_indices();
+ while let Some((i, ch)) = chars.clone().next() {
+ println!("{}: {}", i, ch);
+ }
+}
+
+fn issue6491() {
+ // Used in outer loop, needs &mut
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+
+ // This is fine, inner loop uses a new iterator.
+ let mut it = 1..40;
+ while let Some(n) = it.next() {
+ let mut it = 1..40;
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Weird binding shouldn't change anything.
+ let (mut it, _) = (1..40, 0);
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+
+ // Used after the loop, needs &mut.
+ let mut it = 1..40;
+ while let Some(m) = it.next() {
+ if m % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("next item {}", it.next().unwrap());
+
+ println!("n still is {}", n);
+ }
+}
+
+fn issue6231() {
+ // Closure in the outer loop, needs &mut
+ let mut it = 1..40;
+ let mut opt = Some(0);
+ while let Some(n) = opt.take().or_else(|| it.next()) {
+ while let Some(m) = it.next() {
+ if n % 10 == 0 {
+ break;
+ }
+ println!("doing something with m: {}", m);
+ }
+ println!("n still is {}", n);
+ }
+}
+
+fn issue1924() {
+ struct S<T>(T);
+ impl<T: Iterator<Item = u32>> S<T> {
+ fn f(&mut self) -> Option<u32> {
+ // Used as a field.
+ while let Some(i) = self.0.next() {
+ if !(3..8).contains(&i) {
+ return Some(i);
+ }
+ }
+ None
+ }
+
+ fn f2(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.next() {
+ if i == 1 {
+ return self.f();
+ }
+ }
+ None
+ }
+ }
+ impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
+ fn f3(&mut self) -> Option<u32> {
+ // Don't lint, self borrowed inside the loop
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.0.f();
+ }
+ }
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.f3();
+ }
+ }
+ // This one is fine, a different field is borrowed
+ while let Some(i) = self.0.0.0.next() {
+ if i == 1 {
+ return self.0.1.take();
+ } else {
+ self.0.1 = Some(i);
+ }
+ }
+ None
+ }
+ }
+
+ struct S2<T>(T, u32);
+ impl<T: Iterator<Item = u32>> Iterator for S2<T> {
+ type Item = u32;
+ fn next(&mut self) -> Option<u32> {
+ self.0.next()
+ }
+ }
+
+ // Don't lint, field of the iterator is accessed in the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == it.1 {
+ break;
+ }
+ }
+
+ // Needs &mut, field of the iterator is accessed after the loop
+ let mut it = S2(1..40, 0);
+ while let Some(n) = it.next() {
+ if n == 0 {
+ break;
+ }
+ }
+ println!("iterator field {}", it.1);
+}
+
+fn issue7249() {
+ let mut it = 0..10;
+ let mut x = || {
+ // Needs &mut, the closure can be called multiple times
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ };
+ x();
+ x();
+}
+
+fn issue7510() {
+ let mut it = 0..10;
+ let it = &mut it;
+ // Needs to reborrow `it` as the binding isn't mutable
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.next().unwrap());
+
+ struct S<T>(T);
+ let mut it = 0..10;
+ let it = S(&mut it);
+ // Needs to reborrow `it.0` as the binding isn't mutable
+ while let Some(x) = it.0.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+ struct S<T>(T);
+ let mut s = S(0..10);
+ // Don't lint. `s.0` is used inside the loop.
+ while let Some(_) = s.0.next() {
+ let _ = &mut s.0;
+ }
+}
+
+fn custom_deref() {
+ struct S1<T> {
+ x: T,
+ }
+ struct S2<T>(S1<T>);
+ impl<T> core::ops::Deref for S2<T> {
+ type Target = S1<T>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<T> core::ops::DerefMut for S2<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ let mut s = S2(S1 { x: 0..10 });
+ while let Some(x) = s.x.next() {
+ println!("{}", x);
+ }
+}
+
+fn issue_8113() {
+ let mut x = [0..10];
+ while let Some(x) = x[0].next() {
+ println!("{}", x);
+ }
+}
+
+fn fn_once_closure() {
+ let mut it = 0..10;
+ (|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })();
+
+ fn f(_: impl FnOnce()) {}
+ let mut it = 0..10;
+ f(|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f2(_: impl FnMut()) {}
+ let mut it = 0..10;
+ f2(|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f3(_: fn()) {}
+ f3(|| {
+ let mut it = 0..10;
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })
+}
+
+fn main() {
+ let mut it = 0..20;
+ while let Some(..) = it.next() {
+ println!("test");
+ }
+}
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
new file mode 100644
index 000000000..3236765e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
@@ -0,0 +1,160 @@
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:16:5
+ |
+LL | while let Option::Some(x) = iter.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
+ |
+ = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:21:5
+ |
+LL | while let Some(x) = iter.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:26:5
+ |
+LL | while let Some(_) = iter.next() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:102:9
+ |
+LL | while let Some([..]) = it.next() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:109:9
+ |
+LL | while let Some([_x]) = it.next() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:122:9
+ |
+LL | while let Some(x @ [_]) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:142:9
+ |
+LL | while let Some(_) = y.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:199:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:210:5
+ |
+LL | while let Some(n) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:212:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:221:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:230:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:247:9
+ |
+LL | while let Some(m) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:262:13
+ |
+LL | while let Some(i) = self.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:294:13
+ |
+LL | while let Some(i) = self.0.0.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:323:5
+ |
+LL | while let Some(n) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:335:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:349:5
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:360:5
+ |
+LL | while let Some(x) = it.0.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:395:5
+ |
+LL | while let Some(x) = s.x.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:402:5
+ |
+LL | while let Some(x) = x[0].next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:410:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:420:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:430:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:440:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:450:5
+ |
+LL | while let Some(..) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
+
+error: aborting due to 26 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.rs b/src/tools/clippy/tests/ui/wild_in_or_pats.rs
new file mode 100644
index 000000000..ad600f125
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wild_in_or_pats.rs
@@ -0,0 +1,36 @@
+#![warn(clippy::wildcard_in_or_patterns)]
+
+fn main() {
+ match "foo" {
+ "a" => {
+ dbg!("matched a");
+ },
+ "bar" | _ => {
+ dbg!("matched (bar or) wild");
+ },
+ };
+ match "foo" {
+ "a" => {
+ dbg!("matched a");
+ },
+ "bar" | "bar2" | _ => {
+ dbg!("matched (bar or bar2 or) wild");
+ },
+ };
+ match "foo" {
+ "a" => {
+ dbg!("matched a");
+ },
+ _ | "bar" | _ => {
+ dbg!("matched (bar or) wild");
+ },
+ };
+ match "foo" {
+ "a" => {
+ dbg!("matched a");
+ },
+ _ | "bar" => {
+ dbg!("matched (bar or) wild");
+ },
+ };
+}
diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.stderr b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr
new file mode 100644
index 000000000..45b87aa0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr
@@ -0,0 +1,35 @@
+error: wildcard pattern covers any other pattern as it will match anyway
+ --> $DIR/wild_in_or_pats.rs:8:9
+ |
+LL | "bar" | _ => {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings`
+ = help: consider handling `_` separately
+
+error: wildcard pattern covers any other pattern as it will match anyway
+ --> $DIR/wild_in_or_pats.rs:16:9
+ |
+LL | "bar" | "bar2" | _ => {
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider handling `_` separately
+
+error: wildcard pattern covers any other pattern as it will match anyway
+ --> $DIR/wild_in_or_pats.rs:24:9
+ |
+LL | _ | "bar" | _ => {
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider handling `_` separately
+
+error: wildcard pattern covers any other pattern as it will match anyway
+ --> $DIR/wild_in_or_pats.rs:32:9
+ |
+LL | _ | "bar" => {
+ | ^^^^^^^^^
+ |
+ = help: consider handling `_` separately
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed
new file mode 100644
index 000000000..3ee4ab48a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed
@@ -0,0 +1,104 @@
+// run-rustfix
+// aux-build:non-exhaustive-enum.rs
+
+#![deny(clippy::wildcard_enum_match_arm)]
+#![allow(
+ unreachable_code,
+ unused_variables,
+ dead_code,
+ clippy::single_match,
+ clippy::wildcard_in_or_patterns,
+ clippy::unnested_or_patterns,
+ clippy::diverging_sub_expression
+)]
+
+extern crate non_exhaustive_enum;
+
+use non_exhaustive_enum::ErrorKind;
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum Color {
+ Red,
+ Green,
+ Blue,
+ Rgb(u8, u8, u8),
+ Cyan,
+}
+
+impl Color {
+ fn is_monochrome(self) -> bool {
+ match self {
+ Color::Red | Color::Green | Color::Blue => true,
+ Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
+ Color::Cyan => false,
+ }
+ }
+}
+
+fn main() {
+ let color = Color::Rgb(0, 0, 127);
+ match color {
+ Color::Red => println!("Red"),
+ Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => eprintln!("Not red"),
+ };
+ match color {
+ Color::Red => println!("Red"),
+ _not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan => eprintln!("Not red"),
+ };
+ let _str = match color {
+ Color::Red => "Red".to_owned(),
+ not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan => format!("{:?}", not_red),
+ };
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Blue => {},
+ Color::Cyan => {},
+ c if c.is_monochrome() => {},
+ Color::Rgb(_, _, _) => {},
+ };
+ let _str = match color {
+ Color::Red => "Red",
+ c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
+ };
+ match color {
+ Color::Rgb(r, _, _) if r > 0 => "Some red",
+ Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => "No red",
+ };
+ match color {
+ Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
+ Color::Rgb(..) => {},
+ };
+ let x: u8 = unimplemented!();
+ match x {
+ 0 => {},
+ 140 => {},
+ _ => {},
+ };
+ // We need to use an enum not defined in this test because non_exhaustive is ignored for the
+ // purposes of dead code analysis within a crate.
+ let error_kind = ErrorKind::NotFound;
+ match error_kind {
+ ErrorKind::NotFound => {},
+ ErrorKind::PermissionDenied | _ => {},
+ }
+ match error_kind {
+ ErrorKind::NotFound => {},
+ ErrorKind::PermissionDenied => {},
+ _ => {},
+ }
+
+ {
+ #![allow(clippy::manual_non_exhaustive)]
+ pub enum Enum {
+ A,
+ B,
+ #[doc(hidden)]
+ __Private,
+ }
+ match Enum::A {
+ Enum::A => (),
+ Enum::B | _ => (),
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs
new file mode 100644
index 000000000..468865504
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs
@@ -0,0 +1,104 @@
+// run-rustfix
+// aux-build:non-exhaustive-enum.rs
+
+#![deny(clippy::wildcard_enum_match_arm)]
+#![allow(
+ unreachable_code,
+ unused_variables,
+ dead_code,
+ clippy::single_match,
+ clippy::wildcard_in_or_patterns,
+ clippy::unnested_or_patterns,
+ clippy::diverging_sub_expression
+)]
+
+extern crate non_exhaustive_enum;
+
+use non_exhaustive_enum::ErrorKind;
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum Color {
+ Red,
+ Green,
+ Blue,
+ Rgb(u8, u8, u8),
+ Cyan,
+}
+
+impl Color {
+ fn is_monochrome(self) -> bool {
+ match self {
+ Color::Red | Color::Green | Color::Blue => true,
+ Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
+ Color::Cyan => false,
+ }
+ }
+}
+
+fn main() {
+ let color = Color::Rgb(0, 0, 127);
+ match color {
+ Color::Red => println!("Red"),
+ _ => eprintln!("Not red"),
+ };
+ match color {
+ Color::Red => println!("Red"),
+ _not_red => eprintln!("Not red"),
+ };
+ let _str = match color {
+ Color::Red => "Red".to_owned(),
+ not_red => format!("{:?}", not_red),
+ };
+ match color {
+ Color::Red => {},
+ Color::Green => {},
+ Color::Blue => {},
+ Color::Cyan => {},
+ c if c.is_monochrome() => {},
+ Color::Rgb(_, _, _) => {},
+ };
+ let _str = match color {
+ Color::Red => "Red",
+ c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
+ };
+ match color {
+ Color::Rgb(r, _, _) if r > 0 => "Some red",
+ _ => "No red",
+ };
+ match color {
+ Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
+ Color::Rgb(..) => {},
+ };
+ let x: u8 = unimplemented!();
+ match x {
+ 0 => {},
+ 140 => {},
+ _ => {},
+ };
+ // We need to use an enum not defined in this test because non_exhaustive is ignored for the
+ // purposes of dead code analysis within a crate.
+ let error_kind = ErrorKind::NotFound;
+ match error_kind {
+ ErrorKind::NotFound => {},
+ _ => {},
+ }
+ match error_kind {
+ ErrorKind::NotFound => {},
+ ErrorKind::PermissionDenied => {},
+ _ => {},
+ }
+
+ {
+ #![allow(clippy::manual_non_exhaustive)]
+ pub enum Enum {
+ A,
+ B,
+ #[doc(hidden)]
+ __Private,
+ }
+ match Enum::A {
+ Enum::A => (),
+ _ => (),
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr
new file mode 100644
index 000000000..d63f20903
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr
@@ -0,0 +1,44 @@
+error: wildcard match will also match any future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:42:9
+ |
+LL | _ => eprintln!("Not red"),
+ | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
+ |
+note: the lint level is defined here
+ --> $DIR/wildcard_enum_match_arm.rs:4:9
+ |
+LL | #![deny(clippy::wildcard_enum_match_arm)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: wildcard match will also match any future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:46:9
+ |
+LL | _not_red => eprintln!("Not red"),
+ | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
+
+error: wildcard match will also match any future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:50:9
+ |
+LL | not_red => format!("{:?}", not_red),
+ | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
+
+error: wildcard match will also match any future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:66:9
+ |
+LL | _ => "No red",
+ | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
+
+error: wildcard matches known variants and will also match future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:83:9
+ |
+LL | _ => {},
+ | ^ help: try this: `ErrorKind::PermissionDenied | _`
+
+error: wildcard matches known variants and will also match future added variants
+ --> $DIR/wildcard_enum_match_arm.rs:101:13
+ |
+LL | _ => (),
+ | ^ help: try this: `Enum::B | _`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed
new file mode 100644
index 000000000..ef55f1c31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed
@@ -0,0 +1,245 @@
+// edition:2015
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
+// the 2015 edition here is needed because edition 2018 changed the module system
+// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
+// no longer detects some of the cases starting with Rust 2018.
+// FIXME: We should likely add another edition 2021 test case for this lint
+
+#![warn(clippy::wildcard_imports)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::foo;
+use crate::mod_mod::inner_mod;
+use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
+#[macro_use]
+use crate::struct_mod::{A, inner_struct_mod};
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
+use wildcard_imports_helper::{ExternA, extern_foo};
+
+use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+ let _ = PreludeModAnywhere;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::foo;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
+ use wildcard_imports_helper::{ExternA, extern_foo};
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::inner_foo, inner2::inner_bar};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test::exported;
+ use crate:: fn_mod::foo;
+
+ exported();
+ foo();
+}
+
+mod super_imports {
+ fn foofoo() {}
+
+ mod should_be_replaced {
+ use super::foofoo;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_inside_function {
+ fn with_super_inside_function() {
+ use super::*;
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod should_be_replaced_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::insidefoo;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod use_explicit_should_be_replaced {
+ use super_imports::foofoo;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod use_double_super_should_be_replaced {
+ mod inner {
+ use super::super::foofoo;
+
+ fn with_double_super() {
+ let _ = foofoo();
+ }
+ }
+ }
+
+ mod use_super_explicit_should_be_replaced {
+ use super::super::super_imports::foofoo;
+
+ fn with_super_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod attestation_should_be_replaced {
+ use super::foofoo;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs
new file mode 100644
index 000000000..b81285142
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_imports.rs
@@ -0,0 +1,246 @@
+// edition:2015
+// run-rustfix
+// aux-build:wildcard_imports_helper.rs
+
+// the 2015 edition here is needed because edition 2018 changed the module system
+// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
+// no longer detects some of the cases starting with Rust 2018.
+// FIXME: We should likely add another edition 2021 test case for this lint
+
+#![warn(clippy::wildcard_imports)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
+#![warn(unused_imports)]
+
+extern crate wildcard_imports_helper;
+
+use crate::fn_mod::*;
+use crate::mod_mod::*;
+use crate::multi_fn_mod::*;
+#[macro_use]
+use crate::struct_mod::*;
+
+#[allow(unused_imports)]
+use wildcard_imports_helper::inner::inner_for_self_import;
+use wildcard_imports_helper::inner::inner_for_self_import::*;
+use wildcard_imports_helper::*;
+
+use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
+
+struct ReadFoo;
+
+impl Read for ReadFoo {
+ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
+ Ok(0)
+ }
+}
+
+mod fn_mod {
+ pub fn foo() {}
+}
+
+mod mod_mod {
+ pub mod inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod multi_fn_mod {
+ pub fn multi_foo() {}
+ pub fn multi_bar() {}
+ pub fn multi_baz() {}
+ pub mod multi_inner_mod {
+ pub fn foo() {}
+ }
+}
+
+mod struct_mod {
+ pub struct A;
+ pub struct B;
+ pub mod inner_struct_mod {
+ pub struct C;
+ }
+
+ #[macro_export]
+ macro_rules! double_struct_import_test {
+ () => {
+ let _ = A;
+ };
+ }
+}
+
+fn main() {
+ foo();
+ multi_foo();
+ multi_bar();
+ multi_inner_mod::foo();
+ inner_mod::foo();
+ extern_foo();
+ inner_extern_bar();
+
+ let _ = A;
+ let _ = inner_struct_mod::C;
+ let _ = ExternA;
+ let _ = PreludeModAnywhere;
+
+ double_struct_import_test!();
+ double_struct_import_test!();
+}
+
+mod in_fn_test {
+ pub use self::inner_exported::*;
+ #[allow(unused_imports)]
+ pub(crate) use self::inner_exported2::*;
+
+ fn test_intern() {
+ use crate::fn_mod::*;
+
+ foo();
+ }
+
+ fn test_extern() {
+ use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ use wildcard_imports_helper::*;
+
+ inner_for_self_import::inner_extern_foo();
+ inner_extern_foo();
+
+ extern_foo();
+
+ let _ = ExternA;
+ }
+
+ fn test_inner_nested() {
+ use self::{inner::*, inner2::*};
+
+ inner_foo();
+ inner_bar();
+ }
+
+ fn test_extern_reexported() {
+ use wildcard_imports_helper::*;
+
+ extern_exported();
+ let _ = ExternExportedStruct;
+ let _ = ExternExportedEnum::A;
+ }
+
+ mod inner_exported {
+ pub fn exported() {}
+ pub struct ExportedStruct;
+ pub enum ExportedEnum {
+ A,
+ }
+ }
+
+ mod inner_exported2 {
+ pub(crate) fn exported2() {}
+ }
+
+ mod inner {
+ pub fn inner_foo() {}
+ }
+
+ mod inner2 {
+ pub fn inner_bar() {}
+ }
+}
+
+fn test_reexported() {
+ use crate::in_fn_test::*;
+
+ exported();
+ let _ = ExportedStruct;
+ let _ = ExportedEnum::A;
+}
+
+#[rustfmt::skip]
+fn test_weird_formatting() {
+ use crate:: in_fn_test:: * ;
+ use crate:: fn_mod::
+ *;
+
+ exported();
+ foo();
+}
+
+mod super_imports {
+ fn foofoo() {}
+
+ mod should_be_replaced {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass {
+ use super::*;
+
+ fn with_super() {
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_inside_function {
+ fn with_super_inside_function() {
+ use super::*;
+ let _ = foofoo();
+ }
+ }
+
+ mod test_should_pass_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod should_be_replaced_further_inside {
+ fn insidefoo() {}
+ mod inner {
+ use super::*;
+ fn with_super() {
+ let _ = insidefoo();
+ }
+ }
+ }
+
+ mod use_explicit_should_be_replaced {
+ use super_imports::*;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod use_double_super_should_be_replaced {
+ mod inner {
+ use super::super::*;
+
+ fn with_double_super() {
+ let _ = foofoo();
+ }
+ }
+ }
+
+ mod use_super_explicit_should_be_replaced {
+ use super::super::super_imports::*;
+
+ fn with_super_explicit() {
+ let _ = foofoo();
+ }
+ }
+
+ mod attestation_should_be_replaced {
+ use super::*;
+
+ fn with_explicit() {
+ let _ = foofoo();
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr
new file mode 100644
index 000000000..626c1754f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr
@@ -0,0 +1,132 @@
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:16:5
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+ |
+ = note: `-D clippy::wildcard-imports` implied by `-D warnings`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:17:5
+ |
+LL | use crate::mod_mod::*;
+ | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:18:5
+ |
+LL | use crate::multi_fn_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:20:5
+ |
+LL | use crate::struct_mod::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:24:5
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:25:5
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:96:13
+ |
+LL | use crate::fn_mod::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:102:75
+ |
+LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
+ | ^ help: try: `inner_extern_foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:103:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:114:20
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^ help: try: `inner::inner_foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:114:30
+ |
+LL | use self::{inner::*, inner2::*};
+ | ^^^^^^^^^ help: try: `inner2::inner_bar`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:121:13
+ |
+LL | use wildcard_imports_helper::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:150:9
+ |
+LL | use crate::in_fn_test::*;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:159:9
+ |
+LL | use crate:: in_fn_test:: * ;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:160:9
+ |
+LL | use crate:: fn_mod::
+ | _________^
+LL | | *;
+ | |_________^ help: try: `crate:: fn_mod::foo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:171:13
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::foofoo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:206:17
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::insidefoo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:214:13
+ |
+LL | use super_imports::*;
+ | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:223:17
+ |
+LL | use super::super::*;
+ | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:232:13
+ |
+LL | use super::super::super_imports::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
+
+error: usage of wildcard import
+ --> $DIR/wildcard_imports.rs:240:13
+ |
+LL | use super::*;
+ | ^^^^^^^^ help: try: `super::foofoo`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs
new file mode 100644
index 000000000..446691744
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_literal.rs
@@ -0,0 +1,43 @@
+#![allow(unused_must_use)]
+#![warn(clippy::write_literal)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ // these should be fine
+ write!(v, "Hello");
+ writeln!(v, "Hello");
+ let world = "world";
+ writeln!(v, "Hello {}", world);
+ writeln!(v, "Hello {world}", world = world);
+ writeln!(v, "3 in hex is {:X}", 3);
+ writeln!(v, "2 + 1 = {:.4}", 3);
+ writeln!(v, "2 + 1 = {:5.4}", 3);
+ writeln!(v, "Debug test {:?}", "hello, world");
+ writeln!(v, "{0:8} {1:>8}", "hello", "world");
+ writeln!(v, "{1:8} {0:>8}", "hello", "world");
+ writeln!(v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
+ writeln!(v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
+ writeln!(v, "{number:>width$}", number = 1, width = 6);
+ writeln!(v, "{number:>0width$}", number = 1, width = 6);
+ writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
+ writeln!(v, "10 / 4 is {}", 2.5);
+ writeln!(v, "2 + 1 = {}", 3);
+
+ // these should throw warnings
+ write!(v, "Hello {}", "world");
+ writeln!(v, "Hello {} {}", world, "world");
+ writeln!(v, "Hello {}", "world");
+
+ // positional args don't change the fact
+ // that we're using a literal -- this should
+ // throw a warning
+ writeln!(v, "{0} {1}", "hello", "world");
+ writeln!(v, "{1} {0}", "hello", "world");
+
+ // named args shouldn't change anything either
+ writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+ writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+}
diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr
new file mode 100644
index 000000000..3c5ec91d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_literal.stderr
@@ -0,0 +1,135 @@
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:30:27
+ |
+LL | write!(v, "Hello {}", "world");
+ | ^^^^^^^
+ |
+ = note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+ |
+LL - write!(v, "Hello {}", "world");
+LL + write!(v, "Hello world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:31:39
+ |
+LL | writeln!(v, "Hello {} {}", world, "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "Hello {} {}", world, "world");
+LL + writeln!(v, "Hello {} world", world);
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:32:29
+ |
+LL | writeln!(v, "Hello {}", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "Hello {}", "world");
+LL + writeln!(v, "Hello world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:37:28
+ |
+LL | writeln!(v, "{0} {1}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{0} {1}", "hello", "world");
+LL + writeln!(v, "hello {1}", "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:37:37
+ |
+LL | writeln!(v, "{0} {1}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{0} {1}", "hello", "world");
+LL + writeln!(v, "{0} world", "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:38:28
+ |
+LL | writeln!(v, "{1} {0}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{1} {0}", "hello", "world");
+LL + writeln!(v, "{1} hello", "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:38:37
+ |
+LL | writeln!(v, "{1} {0}", "hello", "world");
+ | ^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{1} {0}", "hello", "world");
+LL + writeln!(v, "world {0}", "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:41:32
+ |
+LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+LL + writeln!(v, "hello {bar}", bar = "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:41:47
+ |
+LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+LL + writeln!(v, "{foo} world", foo = "hello");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:42:32
+ |
+LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL + writeln!(v, "{bar} hello", bar = "world");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal.rs:42:47
+ |
+LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL + writeln!(v, "world {foo}", foo = "hello");
+ |
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs
new file mode 100644
index 000000000..ba0d7be5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_literal_2.rs
@@ -0,0 +1,27 @@
+#![allow(unused_must_use)]
+#![warn(clippy::write_literal)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ writeln!(v, "{}", "{hello}");
+ writeln!(v, r"{}", r"{hello}");
+ writeln!(v, "{}", '\'');
+ writeln!(v, "{}", '"');
+ writeln!(v, r"{}", '"'); // don't lint
+ writeln!(v, r"{}", '\'');
+ writeln!(
+ v,
+ "some {}",
+ "hello \
+ world!"
+ );
+ writeln!(
+ v,
+ "some {}\
+ {} \\ {}",
+ "1", "2", "3",
+ );
+}
diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr
new file mode 100644
index 000000000..9ff297069
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_literal_2.stderr
@@ -0,0 +1,112 @@
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:9:23
+ |
+LL | writeln!(v, "{}", "{hello}");
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+ |
+LL - writeln!(v, "{}", "{hello}");
+LL + writeln!(v, "{{hello}}");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:10:24
+ |
+LL | writeln!(v, r"{}", r"{hello}");
+ | ^^^^^^^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, r"{}", r"{hello}");
+LL + writeln!(v, r"{{hello}}");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:11:23
+ |
+LL | writeln!(v, "{}", '/'');
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{}", '/'');
+LL + writeln!(v, "'");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:12:23
+ |
+LL | writeln!(v, "{}", '"');
+ | ^^^
+ |
+help: try this
+ |
+LL - writeln!(v, "{}", '"');
+LL + writeln!(v, "/"");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:14:24
+ |
+LL | writeln!(v, r"{}", '/'');
+ | ^^^^
+ |
+help: try this
+ |
+LL - writeln!(v, r"{}", '/'');
+LL + writeln!(v, r"'");
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:18:9
+ |
+LL | / "hello /
+LL | | world!"
+ | |_______________^
+ |
+help: try this
+ |
+LL ~ "some hello /
+LL ~ world!"
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:9
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL ~ "some 1/
+LL ~ {} / {}", "2", "3",
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:14
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL ~ 2 / {}",
+LL ~ "1", "3",
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:19
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL ~ {} / 3",
+LL ~ "1", "2",
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/write_with_newline.rs b/src/tools/clippy/tests/ui/write_with_newline.rs
new file mode 100644
index 000000000..446d6914d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_with_newline.rs
@@ -0,0 +1,59 @@
+// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
+// // run-rustfix
+
+#![allow(clippy::write_literal)]
+#![warn(clippy::write_with_newline)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ // These should fail
+ write!(v, "Hello\n");
+ write!(v, "Hello {}\n", "world");
+ write!(v, "Hello {} {}\n", "world", "#2");
+ write!(v, "{}\n", 1265);
+ write!(v, "\n");
+
+ // These should be fine
+ write!(v, "");
+ write!(v, "Hello");
+ writeln!(v, "Hello");
+ writeln!(v, "Hello\n");
+ writeln!(v, "Hello {}\n", "world");
+ write!(v, "Issue\n{}", 1265);
+ write!(v, "{}", 1265);
+ write!(v, "\n{}", 1275);
+ write!(v, "\n\n");
+ write!(v, "like eof\n\n");
+ write!(v, "Hello {} {}\n\n", "world", "#2");
+ writeln!(v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ writeln!(v, "\nbla\n\n"); // #3126
+
+ // Escaping
+ write!(v, "\\n"); // #3514
+ write!(v, "\\\n"); // should fail
+ write!(v, "\\\\n");
+
+ // Raw strings
+ write!(v, r"\n"); // #3778
+
+ // Literal newlines should also fail
+ write!(
+ v,
+ "
+"
+ );
+ write!(
+ v,
+ r"
+"
+ );
+
+ // Don't warn on CRLF (#4208)
+ write!(v, "\r\n");
+ write!(v, "foo\r\n");
+ write!(v, "\\r\n"); //~ ERROR
+ write!(v, "foo\rbar\n");
+}
diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr
new file mode 100644
index 000000000..5f55431be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/write_with_newline.stderr
@@ -0,0 +1,133 @@
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:13:5
+ |
+LL | write!(v, "Hello/n");
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::write-with-newline` implied by `-D warnings`
+help: use `writeln!()` instead
+ |
+LL - write!(v, "Hello/n");
+LL + writeln!(v, "Hello");
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:14:5
+ |
+LL | write!(v, "Hello {}/n", "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "Hello {}/n", "world");
+LL + writeln!(v, "Hello {}", "world");
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:15:5
+ |
+LL | write!(v, "Hello {} {}/n", "world", "#2");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "Hello {} {}/n", "world", "#2");
+LL + writeln!(v, "Hello {} {}", "world", "#2");
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:16:5
+ |
+LL | write!(v, "{}/n", 1265);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "{}/n", 1265);
+LL + writeln!(v, "{}", 1265);
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:17:5
+ |
+LL | write!(v, "/n");
+ | ^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "/n");
+LL + writeln!(v);
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:36:5
+ |
+LL | write!(v, "//n"); // should fail
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "//n"); // should fail
+LL + writeln!(v, "/"); // should fail
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:43:5
+ |
+LL | / write!(
+LL | | v,
+LL | | "
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `writeln!()` instead
+ |
+LL ~ writeln!(
+LL | v,
+LL ~ ""
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:48:5
+ |
+LL | / write!(
+LL | | v,
+LL | | r"
+LL | | "
+LL | | );
+ | |_____^
+ |
+help: use `writeln!()` instead
+ |
+LL ~ writeln!(
+LL | v,
+LL ~ r""
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:57:5
+ |
+LL | write!(v, "/r/n"); //~ ERROR
+ | ^^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "/r/n"); //~ ERROR
+LL + writeln!(v, "/r"); //~ ERROR
+ |
+
+error: using `write!()` with a format string that ends in a single newline
+ --> $DIR/write_with_newline.rs:58:5
+ |
+LL | write!(v, "foo/rbar/n");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use `writeln!()` instead
+ |
+LL - write!(v, "foo/rbar/n");
+LL + writeln!(v, "foo/rbar");
+ |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.fixed b/src/tools/clippy/tests/ui/writeln_empty_string.fixed
new file mode 100644
index 000000000..e7d94acd1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/writeln_empty_string.fixed
@@ -0,0 +1,20 @@
+// run-rustfix
+
+#![allow(unused_must_use)]
+#![warn(clippy::writeln_empty_string)]
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ // These should fail
+ writeln!(v);
+
+ let mut suggestion = Vec::new();
+ writeln!(suggestion);
+
+ // These should be fine
+ writeln!(v);
+ writeln!(v, " ");
+ write!(v, "");
+}
diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.rs b/src/tools/clippy/tests/ui/writeln_empty_string.rs
new file mode 100644
index 000000000..662c62f02
--- /dev/null
+++ b/src/tools/clippy/tests/ui/writeln_empty_string.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+
+#![allow(unused_must_use)]
+#![warn(clippy::writeln_empty_string)]
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ // These should fail
+ writeln!(v, "");
+
+ let mut suggestion = Vec::new();
+ writeln!(suggestion, "");
+
+ // These should be fine
+ writeln!(v);
+ writeln!(v, " ");
+ write!(v, "");
+}
diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.stderr b/src/tools/clippy/tests/ui/writeln_empty_string.stderr
new file mode 100644
index 000000000..ac65aadfc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/writeln_empty_string.stderr
@@ -0,0 +1,16 @@
+error: using `writeln!(v, "")`
+ --> $DIR/writeln_empty_string.rs:11:5
+ |
+LL | writeln!(v, "");
+ | ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)`
+ |
+ = note: `-D clippy::writeln-empty-string` implied by `-D warnings`
+
+error: using `writeln!(suggestion, "")`
+ --> $DIR/writeln_empty_string.rs:14:5
+ |
+LL | writeln!(suggestion, "");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs
new file mode 100644
index 000000000..e3cc90ee2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs
@@ -0,0 +1,206 @@
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+#[derive(Clone, Copy)]
+struct Foo;
+
+impl Foo {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn from_i32(self) {}
+
+ pub fn as_i64(self) {}
+ pub fn into_i64(self) {}
+ pub fn is_i64(self) {}
+ pub fn to_i64(self) {}
+ pub fn from_i64(self) {}
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ pub fn from_cake(self) {}
+
+ fn as_x<F: AsRef<Self>>(_: F) {}
+ fn as_y<F: AsRef<Foo>>(_: F) {}
+}
+
+struct Bar;
+
+impl Bar {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(&self) {}
+ fn into_u32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn to_u32(&self) {}
+ fn from_i32(self) {}
+
+ pub fn as_i64(self) {}
+ pub fn into_i64(&self) {}
+ pub fn is_i64(self) {}
+ pub fn to_i64(self) {}
+ pub fn from_i64(self) {}
+
+ // test for false positives
+ fn as_(self) {}
+ fn into_(&self) {}
+ fn is_(self) {}
+ fn to_(self) {}
+ fn from_(self) {}
+ fn to_mut(&mut self) {}
+}
+
+// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value
+#[allow(clippy::boxed_local)]
+mod issue4293 {
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ struct T;
+
+ impl T {
+ fn into_s1(self: Box<Self>) {}
+ fn into_s2(self: Rc<Self>) {}
+ fn into_s3(self: Arc<Self>) {}
+
+ fn into_t1(self: Box<T>) {}
+ fn into_t2(self: Rc<T>) {}
+ fn into_t3(self: Arc<T>) {}
+ }
+}
+
+// False positive for async (see #4037)
+mod issue4037 {
+ pub struct Foo;
+ pub struct Bar;
+
+ impl Foo {
+ pub async fn into_bar(self) -> Bar {
+ Bar
+ }
+ }
+}
+
+// Lint also in trait definition (see #6307)
+mod issue6307 {
+ trait T: Sized {
+ fn as_i32(self) {}
+ fn as_u32(&self) {}
+ fn into_i32(self) {}
+ fn into_i32_ref(&self) {}
+ fn into_u32(self) {}
+ fn is_i32(self) {}
+ fn is_u32(&self) {}
+ fn to_i32(self) {}
+ fn to_u32(&self) {}
+ fn from_i32(self) {}
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self) {}
+
+ // test for false positives
+ fn as_(self) {}
+ fn into_(&self) {}
+ fn is_(self) {}
+ fn to_(self) {}
+ fn from_(self) {}
+ fn to_mut(&mut self) {}
+ }
+
+ trait U {
+ fn as_i32(self);
+ fn as_u32(&self);
+ fn into_i32(self);
+ fn into_i32_ref(&self);
+ fn into_u32(self);
+ fn is_i32(self);
+ fn is_u32(&self);
+ fn to_i32(self);
+ fn to_u32(&self);
+ fn from_i32(self);
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self);
+
+ // test for false positives
+ fn as_(self);
+ fn into_(&self);
+ fn is_(self);
+ fn to_(self);
+ fn from_(self);
+ fn to_mut(&mut self);
+ }
+
+ trait C: Copy {
+ fn as_i32(self);
+ fn as_u32(&self);
+ fn into_i32(self);
+ fn into_i32_ref(&self);
+ fn into_u32(self);
+ fn is_i32(self);
+ fn is_u32(&self);
+ fn to_i32(self);
+ fn to_u32(&self);
+ fn from_i32(self);
+ // check whether the lint can be allowed at the function level
+ #[allow(clippy::wrong_self_convention)]
+ fn from_cake(self);
+
+ // test for false positives
+ fn as_(self);
+ fn into_(&self);
+ fn is_(self);
+ fn to_(self);
+ fn from_(self);
+ fn to_mut(&mut self);
+ }
+}
+
+mod issue6727 {
+ #[derive(Clone, Copy)]
+ struct FooCopy;
+
+ impl FooCopy {
+ fn to_u64(self) -> u64 {
+ 1
+ }
+ // trigger lint
+ fn to_u64_v2(&self) -> u64 {
+ 1
+ }
+ }
+
+ struct FooNoCopy;
+
+ impl FooNoCopy {
+ // trigger lint
+ fn to_u64(self) -> u64 {
+ 2
+ }
+ fn to_u64_v2(&self) -> u64 {
+ 2
+ }
+ }
+}
+
+pub mod issue8142 {
+ struct S;
+
+ impl S {
+ // Should not lint: "no self at all" is allowed.
+ fn is_forty_two(x: u32) -> bool {
+ x == 42
+ }
+
+ // Should not lint: &self is allowed.
+ fn is_test_code(&self) -> bool {
+ true
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr
new file mode 100644
index 000000000..2e7ee51d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr
@@ -0,0 +1,195 @@
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:16:17
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:22:21
+ |
+LL | pub fn from_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
+ --> $DIR/wrong_self_convention.rs:34:15
+ |
+LL | fn as_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:36:17
+ |
+LL | fn into_i32(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
+ --> $DIR/wrong_self_convention.rs:38:15
+ |
+LL | fn is_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_convention.rs:40:15
+ |
+LL | fn to_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:42:17
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
+ --> $DIR/wrong_self_convention.rs:44:19
+ |
+LL | pub fn as_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:45:21
+ |
+LL | pub fn into_i64(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
+ --> $DIR/wrong_self_convention.rs:46:19
+ |
+LL | pub fn is_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_convention.rs:47:19
+ |
+LL | pub fn to_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:48:21
+ |
+LL | pub fn from_i64(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
+ --> $DIR/wrong_self_convention.rs:93:19
+ |
+LL | fn as_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:96:25
+ |
+LL | fn into_i32_ref(&self) {}
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
+ --> $DIR/wrong_self_convention.rs:98:19
+ |
+LL | fn is_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:102:21
+ |
+LL | fn from_i32(self) {}
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
+ --> $DIR/wrong_self_convention.rs:117:19
+ |
+LL | fn as_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:120:25
+ |
+LL | fn into_i32_ref(&self);
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
+ --> $DIR/wrong_self_convention.rs:122:19
+ |
+LL | fn is_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:126:21
+ |
+LL | fn from_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `into_*` usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:144:25
+ |
+LL | fn into_i32_ref(&self);
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention.rs:150:21
+ |
+LL | fn from_i32(self);
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:174:22
+ |
+LL | fn to_u64_v2(&self) -> u64 {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_convention.rs:183:19
+ |
+LL | fn to_u64(self) -> u64 {
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.rs b/src/tools/clippy/tests/ui/wrong_self_convention2.rs
new file mode 100644
index 000000000..0dcf4743e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_convention2.rs
@@ -0,0 +1,116 @@
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6983 {
+ pub struct Thing;
+ pub trait Trait {
+ fn to_thing(&self) -> Thing;
+ }
+
+ impl Trait for u8 {
+ // don't trigger, e.g. `ToString` from `std` requires `&self`
+ fn to_thing(&self) -> Thing {
+ Thing
+ }
+ }
+
+ trait ToU64 {
+ fn to_u64(self) -> u64;
+ }
+
+ struct FooNoCopy;
+ // don't trigger
+ impl ToU64 for FooNoCopy {
+ fn to_u64(self) -> u64 {
+ 2
+ }
+ }
+}
+
+mod issue7032 {
+ trait Foo {
+ fn from_usize(x: usize) -> Self;
+ }
+ // don't trigger
+ impl Foo for usize {
+ fn from_usize(x: usize) -> Self {
+ x
+ }
+ }
+}
+
+mod issue7179 {
+ pub struct S(i32);
+
+ impl S {
+ // don't trigger (`s` is not `self`)
+ pub fn from_be(s: Self) -> Self {
+ S(i32::from_be(s.0))
+ }
+
+ // lint
+ pub fn from_be_self(self) -> Self {
+ S(i32::from_be(self.0))
+ }
+ }
+
+ trait T {
+ // don't trigger (`s` is not `self`)
+ fn from_be(s: Self) -> Self;
+ // lint
+ fn from_be_self(self) -> Self;
+ }
+
+ trait Foo: Sized {
+ fn as_byte_slice(slice: &[Self]) -> &[u8];
+ }
+}
+
+mod issue3414 {
+ struct CellLikeThing<T>(T);
+
+ impl<T> CellLikeThing<T> {
+ // don't trigger
+ fn into_inner(this: Self) -> T {
+ this.0
+ }
+ }
+
+ impl<T> std::ops::Deref for CellLikeThing<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+ }
+}
+
+// don't trigger
+mod issue4546 {
+ use std::pin::Pin;
+
+ struct S;
+ impl S {
+ pub fn as_mut(self: Pin<&mut Self>) {}
+
+ pub fn as_other_thingy(self: Pin<&Self>) {}
+
+ pub fn is_other_thingy(self: Pin<&Self>) {}
+
+ pub fn to_mut(self: Pin<&mut Self>) {}
+
+ pub fn to_other_thingy(self: Pin<&Self>) {}
+ }
+}
+
+mod issue_8480_8513 {
+ struct Cat(String);
+
+ impl Cat {
+ fn is_animal(&mut self) -> bool {
+ todo!();
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.stderr b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr
new file mode 100644
index 000000000..5bdc47f91
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr
@@ -0,0 +1,19 @@
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention2.rs:54:29
+ |
+LL | pub fn from_be_self(self) -> Self {
+ | ^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods called `from_*` usually take no `self`
+ --> $DIR/wrong_self_convention2.rs:63:25
+ |
+LL | fn from_be_self(self) -> Self;
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs
new file mode 100644
index 000000000..5bb2116bd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs
@@ -0,0 +1,29 @@
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6758 {
+ pub enum Test<T> {
+ One(T),
+ Many(Vec<T>),
+ }
+
+ impl<T> Test<T> {
+ // If a method starts with `to_` and not ends with `_mut` it should expect `&self`
+ pub fn to_many(&mut self) -> Option<&mut [T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+
+ // If a method starts with `to_` and ends with `_mut` it should expect `&mut self`
+ pub fn to_many_mut(&self) -> Option<&[T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr
new file mode 100644
index 000000000..8665d8dc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr
@@ -0,0 +1,19 @@
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_conventions_mut.rs:14:24
+ |
+LL | pub fn to_many(&mut self) -> Option<&mut [T]> {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference
+ --> $DIR/wrong_self_conventions_mut.rs:22:28
+ |
+LL | pub fn to_many_mut(&self) -> Option<&[T]> {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_div_zero.rs b/src/tools/clippy/tests/ui/zero_div_zero.rs
new file mode 100644
index 000000000..968c58f40
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_div_zero.rs
@@ -0,0 +1,13 @@
+#[allow(unused_variables, clippy::eq_op)]
+#[warn(clippy::zero_divided_by_zero)]
+fn main() {
+ let nan = 0.0 / 0.0;
+ let f64_nan = 0.0 / 0.0f64;
+ let other_f64_nan = 0.0f64 / 0.0;
+ let one_more_f64_nan = 0.0f64 / 0.0f64;
+ let zero = 0.0;
+ let other_zero = 0.0;
+ let other_nan = zero / other_zero; // fine - this lint doesn't propagate constants.
+ let not_nan = 2.0 / 0.0; // not an error: 2/0 = inf
+ let also_not_nan = 0.0 / 2.0; // not an error: 0/2 = 0
+}
diff --git a/src/tools/clippy/tests/ui/zero_div_zero.stderr b/src/tools/clippy/tests/ui/zero_div_zero.stderr
new file mode 100644
index 000000000..86563542e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_div_zero.stderr
@@ -0,0 +1,35 @@
+error: constant division of `0.0` with `0.0` will always result in NaN
+ --> $DIR/zero_div_zero.rs:4:15
+ |
+LL | let nan = 0.0 / 0.0;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings`
+ = help: consider using `f64::NAN` if you would like a constant representing NaN
+
+error: constant division of `0.0` with `0.0` will always result in NaN
+ --> $DIR/zero_div_zero.rs:5:19
+ |
+LL | let f64_nan = 0.0 / 0.0f64;
+ | ^^^^^^^^^^^^
+ |
+ = help: consider using `f64::NAN` if you would like a constant representing NaN
+
+error: constant division of `0.0` with `0.0` will always result in NaN
+ --> $DIR/zero_div_zero.rs:6:25
+ |
+LL | let other_f64_nan = 0.0f64 / 0.0;
+ | ^^^^^^^^^^^^
+ |
+ = help: consider using `f64::NAN` if you would like a constant representing NaN
+
+error: constant division of `0.0` with `0.0` will always result in NaN
+ --> $DIR/zero_div_zero.rs:7:28
+ |
+LL | let one_more_f64_nan = 0.0f64 / 0.0f64;
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider using `f64::NAN` if you would like a constant representing NaN
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs
new file mode 100644
index 000000000..fd9ac1fa7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_offset.rs
@@ -0,0 +1,19 @@
+#[allow(clippy::borrow_as_ptr)]
+fn main() {
+ unsafe {
+ let m = &mut () as *mut ();
+ m.offset(0);
+ m.wrapping_add(0);
+ m.sub(0);
+ m.wrapping_sub(0);
+
+ let c = &() as *const ();
+ c.offset(0);
+ c.wrapping_add(0);
+ c.sub(0);
+ c.wrapping_sub(0);
+
+ let sized = &1 as *const i32;
+ sized.offset(0);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr
new file mode 100644
index 000000000..481a44657
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_offset.stderr
@@ -0,0 +1,52 @@
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:5:9
+ |
+LL | m.offset(0);
+ | ^^^^^^^^^^^
+ |
+ = note: `#[deny(clippy::zst_offset)]` on by default
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:6:9
+ |
+LL | m.wrapping_add(0);
+ | ^^^^^^^^^^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:7:9
+ |
+LL | m.sub(0);
+ | ^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:8:9
+ |
+LL | m.wrapping_sub(0);
+ | ^^^^^^^^^^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:11:9
+ |
+LL | c.offset(0);
+ | ^^^^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:12:9
+ |
+LL | c.wrapping_add(0);
+ | ^^^^^^^^^^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:13:9
+ |
+LL | c.sub(0);
+ | ^^^^^^^^
+
+error: offset calculation on zero-sized value
+ --> $DIR/zero_offset.rs:14:9
+ |
+LL | c.wrapping_sub(0);
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed
new file mode 100644
index 000000000..489aa4121
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_ptr.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+pub fn foo(_const: *const f32, _mut: *mut i64) {}
+
+fn main() {
+ let _ = std::ptr::null::<usize>();
+ let _ = std::ptr::null_mut::<f64>();
+ let _: *const u8 = std::ptr::null();
+
+ foo(0 as _, 0 as _);
+ foo(std::ptr::null(), std::ptr::null_mut());
+
+ let z = 0;
+ let _ = z as *const usize; // this is currently not caught
+}
diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs
new file mode 100644
index 000000000..c3b55ef9e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_ptr.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+pub fn foo(_const: *const f32, _mut: *mut i64) {}
+
+fn main() {
+ let _ = 0 as *const usize;
+ let _ = 0 as *mut f64;
+ let _: *const u8 = 0 as *const _;
+
+ foo(0 as _, 0 as _);
+ foo(0 as *const _, 0 as *mut _);
+
+ let z = 0;
+ let _ = z as *const usize; // this is currently not caught
+}
diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr
new file mode 100644
index 000000000..4ee5e9a26
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_ptr.stderr
@@ -0,0 +1,34 @@
+error: `0 as *const _` detected
+ --> $DIR/zero_ptr.rs:5:13
+ |
+LL | let _ = 0 as *const usize;
+ | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null::<usize>()`
+ |
+ = note: `-D clippy::zero-ptr` implied by `-D warnings`
+
+error: `0 as *mut _` detected
+ --> $DIR/zero_ptr.rs:6:13
+ |
+LL | let _ = 0 as *mut f64;
+ | ^^^^^^^^^^^^^ help: try: `std::ptr::null_mut::<f64>()`
+
+error: `0 as *const _` detected
+ --> $DIR/zero_ptr.rs:7:24
+ |
+LL | let _: *const u8 = 0 as *const _;
+ | ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
+
+error: `0 as *const _` detected
+ --> $DIR/zero_ptr.rs:10:9
+ |
+LL | foo(0 as *const _, 0 as *mut _);
+ | ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
+
+error: `0 as *mut _` detected
+ --> $DIR/zero_ptr.rs:10:24
+ |
+LL | foo(0 as *const _, 0 as *mut _);
+ | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs
new file mode 100644
index 000000000..5cd254787
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::zero_sized_map_values)]
+use std::collections::BTreeMap;
+
+const CONST_OK: Option<BTreeMap<String, usize>> = None;
+const CONST_NOT_OK: Option<BTreeMap<String, ()>> = None;
+
+static STATIC_OK: Option<BTreeMap<String, usize>> = None;
+static STATIC_NOT_OK: Option<BTreeMap<String, ()>> = None;
+
+type OkMap = BTreeMap<String, usize>;
+type NotOkMap = BTreeMap<String, ()>;
+
+enum TestEnum {
+ Ok(BTreeMap<String, usize>),
+ NotOk(BTreeMap<String, ()>),
+}
+
+struct Test {
+ ok: BTreeMap<String, usize>,
+ not_ok: BTreeMap<String, ()>,
+
+ also_not_ok: Vec<BTreeMap<usize, ()>>,
+}
+
+trait TestTrait {
+ type Output;
+
+ fn produce_output() -> Self::Output;
+
+ fn weird_map(&self, map: BTreeMap<usize, ()>);
+}
+
+impl Test {
+ fn ok(&self) -> BTreeMap<String, usize> {
+ todo!()
+ }
+
+ fn not_ok(&self) -> BTreeMap<String, ()> {
+ todo!()
+ }
+}
+
+impl TestTrait for Test {
+ type Output = BTreeMap<String, ()>;
+
+ fn produce_output() -> Self::Output {
+ todo!();
+ }
+
+ fn weird_map(&self, map: BTreeMap<usize, ()>) {
+ todo!();
+ }
+}
+
+fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+ todo!();
+}
+
+fn test2(map: BTreeMap<String, usize>, key: &str) -> BTreeMap<String, usize> {
+ todo!();
+}
+
+fn main() {
+ let _: BTreeMap<String, ()> = BTreeMap::new();
+ let _: BTreeMap<String, usize> = BTreeMap::new();
+
+ let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect();
+}
diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr
new file mode 100644
index 000000000..d924f3379
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr
@@ -0,0 +1,107 @@
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:5:28
+ |
+LL | const CONST_NOT_OK: Option<BTreeMap<String, ()>> = None;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::zero-sized-map-values` implied by `-D warnings`
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:8:30
+ |
+LL | static STATIC_NOT_OK: Option<BTreeMap<String, ()>> = None;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:11:17
+ |
+LL | type NotOkMap = BTreeMap<String, ()>;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:15:11
+ |
+LL | NotOk(BTreeMap<String, ()>),
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:20:13
+ |
+LL | not_ok: BTreeMap<String, ()>,
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:22:22
+ |
+LL | also_not_ok: Vec<BTreeMap<usize, ()>>,
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:30:30
+ |
+LL | fn weird_map(&self, map: BTreeMap<usize, ()>);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:38:25
+ |
+LL | fn not_ok(&self) -> BTreeMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:55:14
+ |
+LL | fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:55:50
+ |
+LL | fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:64:35
+ |
+LL | let _: BTreeMap<String, ()> = BTreeMap::new();
+ | ^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:64:12
+ |
+LL | let _: BTreeMap<String, ()> = BTreeMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_btreemap_values.rs:67:12
+ |
+LL | let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect();
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
new file mode 100644
index 000000000..a1608d863
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::zero_sized_map_values)]
+use std::collections::HashMap;
+
+const CONST_OK: Option<HashMap<String, usize>> = None;
+const CONST_NOT_OK: Option<HashMap<String, ()>> = None;
+
+static STATIC_OK: Option<HashMap<String, usize>> = None;
+static STATIC_NOT_OK: Option<HashMap<String, ()>> = None;
+
+type OkMap = HashMap<String, usize>;
+type NotOkMap = HashMap<String, ()>;
+
+enum TestEnum {
+ Ok(HashMap<String, usize>),
+ NotOk(HashMap<String, ()>),
+}
+
+struct Test {
+ ok: HashMap<String, usize>,
+ not_ok: HashMap<String, ()>,
+
+ also_not_ok: Vec<HashMap<usize, ()>>,
+}
+
+trait TestTrait {
+ type Output;
+
+ fn produce_output() -> Self::Output;
+
+ fn weird_map(&self, map: HashMap<usize, ()>);
+}
+
+impl Test {
+ fn ok(&self) -> HashMap<String, usize> {
+ todo!()
+ }
+
+ fn not_ok(&self) -> HashMap<String, ()> {
+ todo!()
+ }
+}
+
+impl TestTrait for Test {
+ type Output = HashMap<String, ()>;
+
+ fn produce_output() -> Self::Output {
+ todo!();
+ }
+
+ fn weird_map(&self, map: HashMap<usize, ()>) {
+ todo!();
+ }
+}
+
+fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+ todo!();
+}
+
+fn test2(map: HashMap<String, usize>, key: &str) -> HashMap<String, usize> {
+ todo!();
+}
+
+fn main() {
+ let _: HashMap<String, ()> = HashMap::new();
+ let _: HashMap<String, usize> = HashMap::new();
+
+ let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
+}
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
new file mode 100644
index 000000000..79770bf90
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
@@ -0,0 +1,107 @@
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:5:28
+ |
+LL | const CONST_NOT_OK: Option<HashMap<String, ()>> = None;
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::zero-sized-map-values` implied by `-D warnings`
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:8:30
+ |
+LL | static STATIC_NOT_OK: Option<HashMap<String, ()>> = None;
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:11:17
+ |
+LL | type NotOkMap = HashMap<String, ()>;
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:15:11
+ |
+LL | NotOk(HashMap<String, ()>),
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:20:13
+ |
+LL | not_ok: HashMap<String, ()>,
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:22:22
+ |
+LL | also_not_ok: Vec<HashMap<usize, ()>>,
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:30:30
+ |
+LL | fn weird_map(&self, map: HashMap<usize, ()>);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:38:25
+ |
+LL | fn not_ok(&self) -> HashMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:55:14
+ |
+LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:55:49
+ |
+LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:64:34
+ |
+LL | let _: HashMap<String, ()> = HashMap::new();
+ | ^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:64:12
+ |
+LL | let _: HashMap<String, ()> = HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: map with zero-sized value type
+ --> $DIR/zero_sized_hashmap_values.rs:67:12
+ |
+LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider using a set instead
+
+error: aborting due to 13 previous errors
+