summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/tests/ui
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /src/tools/clippy/tests/ui
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml12
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml6
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs13
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr4
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.toml5
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/a.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/b.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/c.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs28
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr53
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml21
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr44
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr11
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs10
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/src/main.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/.clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml19
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock109
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml10
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr6
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml10
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs4
-rwxr-xr-xsrc/tools/clippy/tests/ui-cargo/update-all-references.sh3
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr6
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs4
-rw-r--r--src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs87
-rw-r--r--src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr68
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed57
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs67
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr49
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.rs11
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.stderr13
-rw-r--r--src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs30
-rw-r--r--src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr22
-rw-r--r--src/tools/clippy/tests/ui-internal/default_lint.rs28
-rw-r--r--src/tools/clippy/tests/ui-internal/default_lint.stderr21
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.rs92
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.stderr85
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed37
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs37
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr33
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed40
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs38
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr32
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_paths.rs27
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_paths.stderr22
-rw-r--r--src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs45
-rw-r--r--src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr21
-rw-r--r--src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs39
-rw-r--r--src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr27
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.fixed29
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.rs29
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.stderr15
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed21
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs21
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr39
-rw-r--r--src/tools/clippy/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs24
-rw-r--r--src/tools/clippy/tests/ui-toml/arithmetic_allowed/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs41
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr25
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml4
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr16
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_append/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/blacklisted_names_replace/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml6
-rw-r--r--src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/dbg_macro/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs39
-rw-r--r--src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr102
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_append/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs12
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs12
-rw-r--r--src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr36
-rw-r--r--src/tools/clippy/tests/ui-toml/expect_used/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs29
-rw-r--r--src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr19
-rw-r--r--src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs6
-rw-r--r--src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr11
-rw-r--r--src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs60
-rw-r--r--src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr43
-rw-r--r--src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/invalid_min_rust_version/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs3
-rw-r--r--src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs16
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr21
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt1
-rw-r--r--src/tools/clippy/tests/ui-toml/lint_decimal_readability/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs23
-rw-r--r--src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs23
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs98
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml10
-rw-r--r--src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs16
-rw-r--r--src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr40
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs18
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/clippy.toml6
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs60
-rw-r--r--src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr94
-rw-r--r--src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs43
-rw-r--r--src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr91
-rw-r--r--src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs9
-rw-r--r--src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr13
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs20
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr46
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml10
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs23
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr54
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml15
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs42
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr132
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs20
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr20
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml6
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr45
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs73
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr197
-rwxr-xr-xsrc/tools/clippy/tests/ui-toml/update-all-references.sh3
-rw-r--r--src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs44
-rw-r--r--src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr82
-rw-r--r--src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs15
-rw-r--r--src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs3
-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
1940 files changed, 125014 insertions, 0 deletions
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml
new file mode 100644
index 000000000..bc8e428f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "cargo_common_metadata_fail"
+version = "0.1.0"
+publish = false
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/clippy.toml
new file mode 100644
index 000000000..de4f04b24
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/clippy.toml
@@ -0,0 +1 @@
+cargo-ignore-publish = true
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr
new file mode 100644
index 000000000..86953142b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr
@@ -0,0 +1,16 @@
+error: package `cargo_common_metadata_fail` is missing `package.description` metadata
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+
+error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata
+
+error: package `cargo_common_metadata_fail` is missing `package.repository` metadata
+
+error: package `cargo_common_metadata_fail` is missing `package.readme` metadata
+
+error: package `cargo_common_metadata_fail` is missing `package.keywords` metadata
+
+error: package `cargo_common_metadata_fail` is missing `package.categories` metadata
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml
new file mode 100644
index 000000000..5005b83f5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "cargo_common_metadata_fail_publish"
+version = "0.1.0"
+publish = ["some-registry-name"]
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr
new file mode 100644
index 000000000..ac1b5e8e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr
@@ -0,0 +1,16 @@
+error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+
+error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata
+
+error: package `cargo_common_metadata_fail_publish` is missing `package.repository` metadata
+
+error: package `cargo_common_metadata_fail_publish` is missing `package.readme` metadata
+
+error: package `cargo_common_metadata_fail_publish` is missing `package.keywords` metadata
+
+error: package `cargo_common_metadata_fail_publish` is missing `package.categories` metadata
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml
new file mode 100644
index 000000000..51858eecd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "cargo_common_metadata_fail_publish_true"
+version = "0.1.0"
+publish = true
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr
new file mode 100644
index 000000000..be32c0dc4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr
@@ -0,0 +1,16 @@
+error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata
+ |
+ = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+
+error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata
+
+error: package `cargo_common_metadata_fail_publish_true` is missing `package.repository` metadata
+
+error: package `cargo_common_metadata_fail_publish_true` is missing `package.readme` metadata
+
+error: package `cargo_common_metadata_fail_publish_true` is missing `package.keywords` metadata
+
+error: package `cargo_common_metadata_fail_publish_true` is missing `package.categories` metadata
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml
new file mode 100644
index 000000000..9f6e51fb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "cargo_common_metadata_pass"
+version = "0.1.0"
+publish = false
+description = "A test package for the cargo_common_metadata lint"
+repository = "https://github.com/someone/cargo_common_metadata"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["metadata", "lint", "clippy"]
+categories = ["development-tools::testing"]
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/clippy.toml
new file mode 100644
index 000000000..de4f04b24
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/clippy.toml
@@ -0,0 +1 @@
+cargo-ignore-publish = true
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml
new file mode 100644
index 000000000..828efee3a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "cargo_common_metadata_pass_publish_empty"
+version = "0.1.0"
+publish = []
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml
new file mode 100644
index 000000000..45a5bf7c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "cargo_common_metadata_pass_publish_false"
+version = "0.1.0"
+publish = false
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs
new file mode 100644
index 000000000..27841e18a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass_publish_false/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml
new file mode 100644
index 000000000..946d1b366
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-both-diff"
+version = "0.1.0"
+rust-version = "1.56"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml
new file mode 100644
index 000000000..abe19b3a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.59"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
new file mode 100644
index 000000000..9a7d802dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
@@ -0,0 +1,16 @@
+warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.59.0` from `clippy.toml`
+
+error: unnecessary structure name repetition
+ --> $DIR/main.rs:6:21
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> $DIR/main.rs:1:9
+ |
+LL | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml
new file mode 100644
index 000000000..46b92a105
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-both-same"
+version = "0.1.0"
+rust-version = "1.57.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml
new file mode 100644
index 000000000..5cccb362c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.57"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
new file mode 100644
index 000000000..a280e1bac
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+ --> $DIR/main.rs:6:21
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> $DIR/main.rs:1:9
+ |
+LL | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml
new file mode 100644
index 000000000..189cc9f68
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-cargo"
+version = "0.1.0"
+rust-version = "1.56.1"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
new file mode 100644
index 000000000..a280e1bac
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+ --> $DIR/main.rs:6:21
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> $DIR/main.rs:1:9
+ |
+LL | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml
new file mode 100644
index 000000000..bdb7f261d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-clippy"
+version = "0.1.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml
new file mode 100644
index 000000000..ddbdbc1fa
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.58"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
new file mode 100644
index 000000000..a280e1bac
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+ --> $DIR/main.rs:6:21
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> $DIR/main.rs:1:9
+ |
+LL | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml
new file mode 100644
index 000000000..84448ea41
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-file-attr"
+version = "0.1.0"
+rust-version = "1.13"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml
new file mode 100644
index 000000000..ea5d80659
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13.0"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs
new file mode 100644
index 000000000..bcbffa82a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs
@@ -0,0 +1,16 @@
+// FIXME: this should produce a warning, because the attribute says 1.58 and the cargo.toml file
+// says 1.13
+
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.58.0"]
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
new file mode 100644
index 000000000..88f6e0092
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+ --> $DIR/main.rs:11:21
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^ help: use the applicable keyword: `Self`
+ |
+note: the lint level is defined here
+ --> $DIR/main.rs:6:9
+ |
+LL | #![deny(clippy::use_self)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml
new file mode 100644
index 000000000..809c0e748
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pass-both-same"
+version = "0.1.0"
+rust-version = "1.13.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml
new file mode 100644
index 000000000..5e8e48b63
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml
new file mode 100644
index 000000000..32d404f84
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pass-cargo"
+version = "0.1.0"
+rust-version = "1.13.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml
new file mode 100644
index 000000000..cc937d6e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "pass-clippy"
+version = "0.1.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml
new file mode 100644
index 000000000..5e8e48b63
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml
new file mode 100644
index 000000000..8ef689880
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pass-file-attr"
+version = "0.1.0"
+rust-version = "1.59"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs
new file mode 100644
index 000000000..27fe4771d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs
@@ -0,0 +1,13 @@
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.13.0"]
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml
new file mode 100644
index 000000000..e9f94594f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "warn-both-diff"
+version = "0.1.0"
+rust-version = "1.56.0"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml
new file mode 100644
index 000000000..5e8e48b63
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs
new file mode 100644
index 000000000..5b91d5508
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+ pub fn bar() -> Foo {
+ Foo
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr
new file mode 100644
index 000000000..eeae5b7b2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr
@@ -0,0 +1,4 @@
+warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.13.0` from `clippy.toml`
+
+warning: 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.toml
new file mode 100644
index 000000000..bf3c817de
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.toml
@@ -0,0 +1,5 @@
+[package]
+name = "duplicate_mod"
+edition = "2021"
+publish = false
+version = "0.1.0"
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/a.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/a.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/a.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/b.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/b.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/b.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/c.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/c.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/c.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
new file mode 100644
index 000000000..6478e65ac
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
@@ -0,0 +1,28 @@
+#![feature(lint_reasons)]
+
+mod a;
+
+mod b;
+#[path = "b.rs"]
+mod b2;
+
+mod c;
+#[path = "c.rs"]
+mod c2;
+#[path = "c.rs"]
+mod c3;
+
+mod from_other_module;
+mod other_module;
+
+mod d;
+#[path = "d.rs"]
+mod d2;
+#[path = "d.rs"]
+#[expect(clippy::duplicate_mod)]
+mod d3;
+#[path = "d.rs"]
+#[allow(clippy::duplicate_mod)]
+mod d4;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
new file mode 100644
index 000000000..b450a2b18
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
@@ -0,0 +1,53 @@
+error: file is loaded as a module multiple times: `$DIR/b.rs`
+ --> $DIR/main.rs:5:1
+ |
+LL | mod b;
+ | ^^^^^^ first loaded here
+LL | / #[path = "b.rs"]
+LL | | mod b2;
+ | |_______^ loaded again here
+ |
+ = note: `-D clippy::duplicate-mod` implied by `-D warnings`
+ = help: replace all but one `mod` item with `use` items
+
+error: file is loaded as a module multiple times: `$DIR/c.rs`
+ --> $DIR/main.rs:9:1
+ |
+LL | mod c;
+ | ^^^^^^ first loaded here
+LL | / #[path = "c.rs"]
+LL | | mod c2;
+ | |_______^ loaded again here
+LL | / #[path = "c.rs"]
+LL | | mod c3;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: file is loaded as a module multiple times: `$DIR/d.rs`
+ --> $DIR/main.rs:18:1
+ |
+LL | mod d;
+ | ^^^^^^ first loaded here
+LL | / #[path = "d.rs"]
+LL | | mod d2;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
+ --> $DIR/main.rs:15:1
+ |
+LL | mod from_other_module;
+ | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
+ |
+ ::: $DIR/other_module/mod.rs:1:1
+ |
+LL | / #[path = "../from_other_module.rs"]
+LL | | mod m;
+ | |______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs
new file mode 100644
index 000000000..36ce7286a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs
@@ -0,0 +1,2 @@
+#[path = "../from_other_module.rs"]
+mod m;
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml
new file mode 100644
index 000000000..97d51462a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml
@@ -0,0 +1,21 @@
+
+# Content that triggers the lint goes here
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[features]
+use-qwq = []
+use_qwq = []
+with-owo = []
+with_owo = []
+qvq-support = []
+qvq_support = []
+no-qaq = []
+no_qaq = []
+not-orz = []
+not_orz = []
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs
new file mode 100644
index 000000000..64f01a98c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+ // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
new file mode 100644
index 000000000..b9e6cb49b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
@@ -0,0 +1,44 @@
+error: the "no-" prefix in the feature name "no-qaq" is negative
+ |
+ = note: `-D clippy::negative-feature-names` implied by `-D warnings`
+ = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "no_" prefix in the feature name "no_qaq" is negative
+ |
+ = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "not-" prefix in the feature name "not-orz" is negative
+ |
+ = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "not_" prefix in the feature name "not_orz" is negative
+ |
+ = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "-support" suffix in the feature name "qvq-support" is redundant
+ |
+ = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
+ = help: consider renaming the feature to "qvq"
+
+error: the "_support" suffix in the feature name "qvq_support" is redundant
+ |
+ = help: consider renaming the feature to "qvq"
+
+error: the "use-" prefix in the feature name "use-qwq" is redundant
+ |
+ = help: consider renaming the feature to "qwq"
+
+error: the "use_" prefix in the feature name "use_qwq" is redundant
+ |
+ = help: consider renaming the feature to "qwq"
+
+error: the "with-" prefix in the feature name "with-owo" is redundant
+ |
+ = help: consider renaming the feature to "owo"
+
+error: the "with_" prefix in the feature name "with_owo" is redundant
+ |
+ = help: consider renaming the feature to "owo"
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml
new file mode 100644
index 000000000..cf947312b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml
@@ -0,0 +1,9 @@
+
+# This file should not trigger the lint
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs b/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs
new file mode 100644
index 000000000..64f01a98c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+ // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml
new file mode 100644
index 000000000..b3d36a9fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs
new file mode 100644
index 000000000..91cd540a2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs
@@ -0,0 +1 @@
+pub mod stuff;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs
new file mode 100644
index 000000000..7713fa9d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs
@@ -0,0 +1,3 @@
+pub mod most;
+
+pub struct Inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs
new file mode 100644
index 000000000..5a5eaf967
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs
@@ -0,0 +1 @@
+pub struct Snarks;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs
new file mode 100644
index 000000000..a12734db7
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs
@@ -0,0 +1,3 @@
+pub mod inner;
+
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs
new file mode 100644
index 000000000..3e985d4e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::self_named_module_files)]
+
+mod bad;
+
+fn main() {
+ let _ = bad::Thing;
+ let _ = bad::inner::stuff::Inner;
+ let _ = bad::inner::stuff::most::Snarks;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
new file mode 100644
index 000000000..e2010e998
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
@@ -0,0 +1,19 @@
+error: `mod.rs` files are required, found `bad/inner.rs`
+ --> $DIR/bad/inner.rs:1:1
+ |
+LL | pub mod stuff;
+ | ^
+ |
+ = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+ = help: move `bad/inner.rs` to `bad/inner/mod.rs`
+
+error: `mod.rs` files are required, found `bad/inner/stuff.rs`
+ --> $DIR/bad/inner/stuff.rs:1:1
+ |
+LL | pub mod most;
+ | ^
+ |
+ = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml
new file mode 100644
index 000000000..3610d13c1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "fail-no-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs
new file mode 100644
index 000000000..f19ab10d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs
new file mode 100644
index 000000000..c6e9045b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::mod_module_files)]
+
+mod bad;
+
+fn main() {
+ let _ = bad::Thing;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
new file mode 100644
index 000000000..f91940209
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
@@ -0,0 +1,11 @@
+error: `mod.rs` files are not allowed, found `bad/mod.rs`
+ --> $DIR/bad/mod.rs:1:1
+ |
+LL | pub struct Thing;
+ | ^
+ |
+ = note: `-D clippy::mod-module-files` implied by `-D warnings`
+ = help: move `bad/mod.rs` to `bad.rs`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml
new file mode 100644
index 000000000..1c2991695
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pass-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs
new file mode 100644
index 000000000..f19ab10d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs
new file mode 100644
index 000000000..9e08715fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::self_named_module_files)]
+
+mod bad;
+mod more;
+
+fn main() {
+ let _ = bad::Thing;
+ let _ = more::foo::Foo;
+ let _ = more::inner::Inner;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs
new file mode 100644
index 000000000..4a835673a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs
@@ -0,0 +1 @@
+pub struct Foo;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs
new file mode 100644
index 000000000..aa84f78cc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs
@@ -0,0 +1 @@
+pub struct Inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs
new file mode 100644
index 000000000..d79569f78
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs
@@ -0,0 +1,2 @@
+pub mod foo;
+pub mod inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml
new file mode 100644
index 000000000..4180aaf51
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pass-no-mod"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs
new file mode 100644
index 000000000..f19ab10d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs
new file mode 100644
index 000000000..50211a340
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::mod_module_files)]
+
+mod good;
+
+fn main() {
+ let _ = good::Thing;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml
new file mode 100644
index 000000000..7eb56cc4e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "no_warn"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/clippy.toml
new file mode 100644
index 000000000..cda8d17ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = false
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/.clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/.clippy.toml
new file mode 100644
index 000000000..cda8d17ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/.clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = false
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.toml
new file mode 100644
index 000000000..b4847d070
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "warn"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/clippy.toml
new file mode 100644
index 000000000..cda8d17ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = false
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
new file mode 100644
index 000000000..98697e001
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
@@ -0,0 +1,2 @@
+Using config file `$SRC_DIR/.clippy.toml`
+Warning: `$SRC_DIR/clippy.toml` will be ignored.
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml
new file mode 100644
index 000000000..278bebbbd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml
@@ -0,0 +1,19 @@
+# Should not lint for dev or build dependencies. See issue 5041.
+
+[package]
+name = "multiple_crate_versions"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+# One of the versions of winapi is only a dev dependency: allowed
+[dependencies]
+ctrlc = "=3.1.0"
+[dev-dependencies]
+ansi_term = "=0.11.0"
+
+# Both versions of winapi are a build dependency: allowed
+[build-dependencies]
+ctrlc = "=3.1.0"
+ansi_term = "=0.11.0"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs
new file mode 100644
index 000000000..1b2d3ec94
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
new file mode 100644
index 000000000..7e96aa36f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
@@ -0,0 +1,109 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "ctrlc"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c"
+dependencies = [
+ "kernel32-sys",
+ "nix",
+ "winapi 0.2.8",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+
+[[package]]
+name = "multiple_crate_versions"
+version = "0.1.0"
+dependencies = [
+ "ansi_term",
+ "ctrlc",
+]
+
+[[package]]
+name = "nix"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "void",
+]
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
new file mode 100644
index 000000000..4f97b0113
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "multiple_crate_versions"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[dependencies]
+ctrlc = "=3.1.0"
+ansi_term = "=0.11.0"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs
new file mode 100644
index 000000000..1b2d3ec94
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
new file mode 100644
index 000000000..f3113e093
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
@@ -0,0 +1,6 @@
+error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
+ |
+ = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml
new file mode 100644
index 000000000..6c46571c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "multiple_crate_versions"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[dependencies]
+regex = "1.3.7"
+serde = "1.0.110"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs
new file mode 100644
index 000000000..1b2d3ec94
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/update-all-references.sh b/src/tools/clippy/tests/ui-cargo/update-all-references.sh
new file mode 100755
index 000000000..4391499a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/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-cargo/wildcard_dependencies/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml
new file mode 100644
index 000000000..3e1a02cbb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "wildcard_dependencies"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[dependencies]
+regex = "*"
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs
new file mode 100644
index 000000000..581babfea
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=wildcard_dependencies
+#![warn(clippy::wildcard_dependencies)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr
new file mode 100644
index 000000000..9e65d2f99
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr
@@ -0,0 +1,6 @@
+error: wildcard dependency for `regex`
+ |
+ = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml
new file mode 100644
index 000000000..f844cab09
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "wildcard_dependencies"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[dependencies]
+regex = "1"
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs
new file mode 100644
index 000000000..581babfea
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=wildcard_dependencies
+#![warn(clippy::wildcard_dependencies)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs
new file mode 100644
index 000000000..31acac89c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs
@@ -0,0 +1,87 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+///////////////////////
+// Valid descriptions
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::VALID_ONE,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "1.29.0"]
+ pub clippy::VALID_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "1.59.0"]
+ pub clippy::VALID_THREE,
+ Warn,
+ "Three",
+ report_in_external_macro: true
+}
+
+///////////////////////
+// Invalid attributes
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version = "1.2.3.4.5.6"]
+ pub clippy::INVALID_ONE,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "I'm a string"]
+ pub clippy::INVALID_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+///////////////////////
+// Missing attribute test
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version]
+ pub clippy::MISSING_ATTRIBUTE_ONE,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub clippy::MISSING_ATTRIBUTE_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+#[allow(clippy::missing_clippy_version_attribute)]
+mod internal_clippy_lints {
+ declare_tool_lint! {
+ pub clippy::ALLOW_MISSING_ATTRIBUTE_ONE,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+ }
+}
+
+use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE;
+declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr
new file mode 100644
index 000000000..533107588
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr
@@ -0,0 +1,68 @@
+error: this item has an invalid `clippy::version` attribute
+ --> $DIR/check_clippy_version_attribute.rs:40:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version = "1.2.3.4.5.6"]
+LL | | pub clippy::INVALID_ONE,
+LL | | Warn,
+LL | | "One",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/check_clippy_version_attribute.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+ = help: please use a valid sematic version, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this item has an invalid `clippy::version` attribute
+ --> $DIR/check_clippy_version_attribute.rs:48:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version = "I'm a string"]
+LL | | pub clippy::INVALID_TWO,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+ = help: please use a valid sematic version, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+ --> $DIR/check_clippy_version_attribute.rs:59:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version]
+LL | | pub clippy::MISSING_ATTRIBUTE_ONE,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+ = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+ = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+ --> $DIR/check_clippy_version_attribute.rs:67:1
+ |
+LL | / declare_tool_lint! {
+LL | | pub clippy::MISSING_ATTRIBUTE_TWO,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+ = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
new file mode 100644
index 000000000..9f299d7de
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
@@ -0,0 +1,57 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate clippy_utils;
+extern crate rustc_ast;
+extern crate rustc_errors;
+extern crate rustc_lint;
+extern crate rustc_session;
+extern crate rustc_span;
+
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
+use rustc_ast::ast::Expr;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+impl EarlyLintPass for Pass {
+ fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
+ let lint_msg = "lint message";
+ let help_msg = "help message";
+ let note_msg = "note message";
+ let sugg = "new_call()";
+ let predicate = true;
+
+ span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable);
+ span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
+ span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
+ span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
+ span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
+
+ // This expr shouldn't trigger this lint.
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.note(note_msg);
+ if predicate {
+ db.note(note_msg);
+ }
+ });
+
+ // Issue #8798
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.help(help_msg).help(help_msg);
+ });
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
new file mode 100644
index 000000000..2b113f555
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate clippy_utils;
+extern crate rustc_ast;
+extern crate rustc_errors;
+extern crate rustc_lint;
+extern crate rustc_session;
+extern crate rustc_span;
+
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
+use rustc_ast::ast::Expr;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+impl EarlyLintPass for Pass {
+ fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
+ let lint_msg = "lint message";
+ let help_msg = "help message";
+ let note_msg = "note message";
+ let sugg = "new_call()";
+ let predicate = true;
+
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
+ });
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.span_help(expr.span, help_msg);
+ });
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.help(help_msg);
+ });
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.span_note(expr.span, note_msg);
+ });
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.note(note_msg);
+ });
+
+ // This expr shouldn't trigger this lint.
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.note(note_msg);
+ if predicate {
+ db.note(note_msg);
+ }
+ });
+
+ // Issue #8798
+ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+ db.help(help_msg).help(help_msg);
+ });
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
new file mode 100644
index 000000000..0852fe65a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
@@ -0,0 +1,49 @@
+error: this call is collapsible
+ --> $DIR/collapsible_span_lint_calls.rs:36:9
+ |
+LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
+LL | | });
+ | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)`
+ |
+note: the lint level is defined here
+ --> $DIR/collapsible_span_lint_calls.rs:2:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
+
+error: this call is collapsible
+ --> $DIR/collapsible_span_lint_calls.rs:39:9
+ |
+LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+LL | | db.span_help(expr.span, help_msg);
+LL | | });
+ | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
+
+error: this call is collapsible
+ --> $DIR/collapsible_span_lint_calls.rs:42:9
+ |
+LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+LL | | db.help(help_msg);
+LL | | });
+ | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
+
+error: this call is collapsible
+ --> $DIR/collapsible_span_lint_calls.rs:45:9
+ |
+LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+LL | | db.span_note(expr.span, note_msg);
+LL | | });
+ | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
+
+error: this call is collapsible
+ --> $DIR/collapsible_span_lint_calls.rs:48:9
+ |
+LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+LL | | db.note(note_msg);
+LL | | });
+ | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
new file mode 100644
index 000000000..5057a0183
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
@@ -0,0 +1,11 @@
+// rustc-env:RUST_BACKTRACE=0
+// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo"
+// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs"
+// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
+
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+
+fn it_looks_like_you_are_trying_to_kill_clippy() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
new file mode 100644
index 000000000..a1b8e2ee1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
@@ -0,0 +1,13 @@
+thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+
+error: internal compiler error: unexpected panic
+
+note: the compiler unexpectedly panicked. this is a bug.
+
+note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
+
+note: Clippy version: foo
+
+query stack during panic:
+end of query stack
diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs
new file mode 100644
index 000000000..c8961d5e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs
@@ -0,0 +1,30 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate clippy_lints;
+use clippy_lints::deprecated_lints::ClippyDeprecatedLint;
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// TODO
+ #[clippy::version = "1.63.0"]
+ pub COOL_LINT_DEFAULT,
+ "default deprecation note"
+}
+
+declare_deprecated_lint! {
+ /// ### What it does
+ /// Nothing. This lint has been deprecated.
+ ///
+ /// ### Deprecation reason
+ /// This lint has been replaced by `cooler_lint`
+ #[clippy::version = "1.63.0"]
+ pub COOL_LINT,
+ "this lint has been replaced by `cooler_lint`"
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr
new file mode 100644
index 000000000..ca26b649f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr
@@ -0,0 +1,22 @@
+error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason
+ --> $DIR/default_deprecation_reason.rs:8:1
+ |
+LL | / declare_deprecated_lint! {
+LL | | /// ### What it does
+LL | | /// Nothing. This lint has been deprecated.
+LL | | ///
+... |
+LL | | "default deprecation note"
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/default_deprecation_reason.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]`
+ = note: this error originates in the macro `declare_deprecated_lint` (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-internal/default_lint.rs b/src/tools/clippy/tests/ui-internal/default_lint.rs
new file mode 100644
index 000000000..da29aedb2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_lint.rs
@@ -0,0 +1,28 @@
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT_DEFAULT,
+ Warn,
+ "default lint description",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/default_lint.stderr b/src/tools/clippy/tests/ui-internal/default_lint.stderr
new file mode 100644
index 000000000..8961bd462
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_lint.stderr
@@ -0,0 +1,21 @@
+error: the lint `TEST_LINT_DEFAULT` has the default lint description
+ --> $DIR/default_lint.rs:18:1
+ |
+LL | / declare_tool_lint! {
+LL | | pub clippy::TEST_LINT_DEFAULT,
+LL | | Warn,
+LL | | "default lint description",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/default_lint.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
+ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (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-internal/if_chain_style.rs b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
new file mode 100644
index 000000000..b0d89e038
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
@@ -0,0 +1,92 @@
+#![warn(clippy::if_chain_style)]
+#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
+
+extern crate if_chain;
+
+use if_chain::if_chain;
+
+fn main() {
+ if true {
+ let x = "";
+ // `if_chain!` inside `if`
+ if_chain! {
+ if true;
+ if true;
+ then {}
+ }
+ }
+ if_chain! {
+ if true
+ // multi-line AND'ed conditions
+ && false;
+ if let Some(1) = Some(1);
+ // `let` before `then`
+ let x = "";
+ then {
+ ();
+ }
+ }
+ if_chain! {
+ // single `if` condition
+ if true;
+ then {
+ let x = "";
+ // nested if
+ if true {}
+ }
+ }
+ if_chain! {
+ // starts with `let ..`
+ let x = "";
+ if let Some(1) = Some(1);
+ then {
+ let x = "";
+ let x = "";
+ // nested if_chain!
+ if_chain! {
+ if true;
+ if true;
+ then {}
+ }
+ }
+ }
+}
+
+fn negative() {
+ if true {
+ ();
+ if_chain! {
+ if true;
+ if true;
+ then { (); }
+ }
+ }
+ if_chain! {
+ if true;
+ let x = "";
+ if true;
+ then { (); }
+ }
+ if_chain! {
+ if true;
+ if true;
+ then {
+ if true { 1 } else { 2 }
+ } else {
+ 3
+ }
+ };
+ if true {
+ if_chain! {
+ if true;
+ if true;
+ then {}
+ }
+ } else if false {
+ if_chain! {
+ if true;
+ if false;
+ then {}
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
new file mode 100644
index 000000000..24106510e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
@@ -0,0 +1,85 @@
+error: this `if` can be part of the inner `if_chain!`
+ --> $DIR/if_chain_style.rs:9:5
+ |
+LL | / if true {
+LL | | let x = "";
+LL | | // `if_chain!` inside `if`
+LL | | if_chain! {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::if-chain-style` implied by `-D warnings`
+help: this `let` statement can also be in the `if_chain!`
+ --> $DIR/if_chain_style.rs:10:9
+ |
+LL | let x = "";
+ | ^^^^^^^^^^^
+
+error: `if a && b;` should be `if a; if b;`
+ --> $DIR/if_chain_style.rs:19:12
+ |
+LL | if true
+ | ____________^
+LL | | // multi-line AND'ed conditions
+LL | | && false;
+ | |____________________^
+
+error: `let` expression should be inside `then { .. }`
+ --> $DIR/if_chain_style.rs:24:9
+ |
+LL | let x = "";
+ | ^^^^^^^^^^^
+
+error: this `if` can be part of the outer `if_chain!`
+ --> $DIR/if_chain_style.rs:35:13
+ |
+LL | if true {}
+ | ^^^^^^^^^^
+ |
+help: this `let` statement can also be in the `if_chain!`
+ --> $DIR/if_chain_style.rs:33:13
+ |
+LL | let x = "";
+ | ^^^^^^^^^^^
+
+error: `if_chain!` only has one `if`
+ --> $DIR/if_chain_style.rs:29:5
+ |
+LL | / if_chain! {
+LL | | // single `if` condition
+LL | | if true;
+LL | | then {
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+ = note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `let` expression should be above the `if_chain!`
+ --> $DIR/if_chain_style.rs:40:9
+ |
+LL | let x = "";
+ | ^^^^^^^^^^^
+
+error: this `if_chain!` can be merged with the outer `if_chain!`
+ --> $DIR/if_chain_style.rs:46:13
+ |
+LL | / if_chain! {
+LL | | if true;
+LL | | if true;
+LL | | then {}
+LL | | }
+ | |_____________^
+ |
+help: these `let` statements can also be in the `if_chain!`
+ --> $DIR/if_chain_style.rs:43:13
+ |
+LL | / let x = "";
+LL | | let x = "";
+ | |_______________________^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
new file mode 100644
index 000000000..eaea218e1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
@@ -0,0 +1,37 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
+#![feature(rustc_private)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::Symbol;
+
+macro_rules! sym {
+ ($tt:tt) => {
+ rustc_span::symbol::Symbol::intern(stringify!($tt))
+ };
+}
+
+fn main() {
+ // Direct use of Symbol::intern
+ let _ = rustc_span::sym::f32;
+
+ // Using a sym macro
+ let _ = rustc_span::sym::f32;
+
+ // Correct suggestion when symbol isn't stringified constant name
+ let _ = rustc_span::sym::proc_dash_macro;
+
+ // interning a keyword
+ let _ = rustc_span::symbol::kw::SelfLower;
+
+ // Interning a symbol that is not defined
+ let _ = Symbol::intern("xyz123");
+ let _ = sym!(xyz123);
+
+ // Using a different `intern` function
+ let _ = intern("f32");
+}
+
+fn intern(_: &str) {}
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
new file mode 100644
index 000000000..7efebb8fa
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
+#![feature(rustc_private)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::Symbol;
+
+macro_rules! sym {
+ ($tt:tt) => {
+ rustc_span::symbol::Symbol::intern(stringify!($tt))
+ };
+}
+
+fn main() {
+ // Direct use of Symbol::intern
+ let _ = Symbol::intern("f32");
+
+ // Using a sym macro
+ let _ = sym!(f32);
+
+ // Correct suggestion when symbol isn't stringified constant name
+ let _ = Symbol::intern("proc-macro");
+
+ // interning a keyword
+ let _ = Symbol::intern("self");
+
+ // Interning a symbol that is not defined
+ let _ = Symbol::intern("xyz123");
+ let _ = sym!(xyz123);
+
+ // Using a different `intern` function
+ let _ = intern("f32");
+}
+
+fn intern(_: &str) {}
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
new file mode 100644
index 000000000..4e99636e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
@@ -0,0 +1,33 @@
+error: interning a defined symbol
+ --> $DIR/interning_defined_symbol.rs:18:13
+ |
+LL | let _ = Symbol::intern("f32");
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
+ |
+note: the lint level is defined here
+ --> $DIR/interning_defined_symbol.rs:2:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
+
+error: interning a defined symbol
+ --> $DIR/interning_defined_symbol.rs:21:13
+ |
+LL | let _ = sym!(f32);
+ | ^^^^^^^^^ help: try: `rustc_span::sym::f32`
+
+error: interning a defined symbol
+ --> $DIR/interning_defined_symbol.rs:24:13
+ |
+LL | let _ = Symbol::intern("proc-macro");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
+
+error: interning a defined symbol
+ --> $DIR/interning_defined_symbol.rs:27:13
+ |
+LL | let _ = Symbol::intern("self");
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
new file mode 100644
index 000000000..900a8fffd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
@@ -0,0 +1,40 @@
+// run-rustfix
+
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate rustc_ast;
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+use clippy_utils::extract_msrv_attr;
+use rustc_hir::Expr;
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+
+declare_lint! {
+ pub TEST_LINT,
+ Warn,
+ ""
+}
+
+struct Pass {
+ msrv: Option<RustcVersion>,
+}
+
+impl_lint_pass!(Pass => [TEST_LINT]);
+
+impl LateLintPass<'_> for Pass {
+ extract_msrv_attr!(LateContext);
+ fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
+}
+
+impl EarlyLintPass for Pass {
+ extract_msrv_attr!(EarlyContext);
+ fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
new file mode 100644
index 000000000..4bc8164db
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate rustc_ast;
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+use clippy_utils::extract_msrv_attr;
+use rustc_hir::Expr;
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+
+declare_lint! {
+ pub TEST_LINT,
+ Warn,
+ ""
+}
+
+struct Pass {
+ msrv: Option<RustcVersion>,
+}
+
+impl_lint_pass!(Pass => [TEST_LINT]);
+
+impl LateLintPass<'_> for Pass {
+ fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
+}
+
+impl EarlyLintPass for Pass {
+ fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr
new file mode 100644
index 000000000..ddc06f0be
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr
@@ -0,0 +1,32 @@
+error: `extract_msrv_attr!` macro missing from `LateLintPass` implementation
+ --> $DIR/invalid_msrv_attr_impl.rs:30:1
+ |
+LL | impl LateLintPass<'_> for Pass {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/invalid_msrv_attr_impl.rs:3:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::missing_msrv_attr_impl)]` implied by `#[deny(clippy::internal)]`
+help: add `extract_msrv_attr!(LateContext)` to the `LateLintPass` implementation
+ |
+LL + impl LateLintPass<'_> for Pass {
+LL + extract_msrv_attr!(LateContext);
+ |
+
+error: `extract_msrv_attr!` macro missing from `EarlyLintPass` implementation
+ --> $DIR/invalid_msrv_attr_impl.rs:34:1
+ |
+LL | impl EarlyLintPass for Pass {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: add `extract_msrv_attr!(EarlyContext)` to the `EarlyLintPass` implementation
+ |
+LL + impl EarlyLintPass for Pass {
+LL + extract_msrv_attr!(EarlyContext);
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.rs b/src/tools/clippy/tests/ui-internal/invalid_paths.rs
new file mode 100644
index 000000000..b823ff7fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/invalid_paths.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+
+mod paths {
+ // Good path
+ pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+
+ // Path to method on inherent impl of a primitive type
+ pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+
+ // Path to method on inherent impl
+ pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
+
+ // Path with empty segment
+ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+
+ // Path with bad crate
+ pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+
+ // Path with bad module
+ pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+
+ // Path to method on an enum inherent impl
+ pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
new file mode 100644
index 000000000..0e8508869
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
@@ -0,0 +1,22 @@
+error: invalid path
+ --> $DIR/invalid_paths.rs:15:5
+ |
+LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::invalid-paths` implied by `-D warnings`
+
+error: invalid path
+ --> $DIR/invalid_paths.rs:18:5
+ |
+LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: invalid path
+ --> $DIR/invalid_paths.rs:21:5
+ |
+LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
new file mode 100644
index 000000000..1fd03cfe3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
@@ -0,0 +1,45 @@
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+use rustc_lint::LintPass;
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT_REGISTERED,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL,
+ Warn,
+ "",
+ report_in_external_macro: true
+}
+
+pub struct Pass;
+impl LintPass for Pass {
+ fn name(&self) -> &'static str {
+ "TEST_LINT"
+ }
+}
+
+declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]);
+
+pub struct Pass3;
+impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
new file mode 100644
index 000000000..de04920b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
@@ -0,0 +1,21 @@
+error: the lint `TEST_LINT` is not added to any `LintPass`
+ --> $DIR/lint_without_lint_pass.rs:12:1
+ |
+LL | / declare_tool_lint! {
+LL | | pub clippy::TEST_LINT,
+LL | | Warn,
+LL | | "",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/lint_without_lint_pass.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]`
+ = note: this error originates in the macro `declare_tool_lint` (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-internal/match_type_on_diag_item.rs b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
new file mode 100644
index 000000000..4b41ff15e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
@@ -0,0 +1,39 @@
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate clippy_utils;
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+
+#[macro_use]
+extern crate rustc_session;
+use clippy_utils::{paths, ty::match_type};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
+
+declare_lint! {
+ pub TEST_LINT,
+ Warn,
+ ""
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+static OPTION: [&str; 3] = ["core", "option", "Option"];
+
+impl<'tcx> LateLintPass<'tcx> for Pass {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ let _ = match_type(cx, ty, &OPTION);
+ let _ = match_type(cx, ty, &["core", "result", "Result"]);
+
+ let rc_path = &["alloc", "rc", "Rc"];
+ let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
new file mode 100644
index 000000000..e3cb6b6c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
@@ -0,0 +1,27 @@
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:31:17
+ |
+LL | let _ = match_type(cx, ty, &OPTION);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)`
+ |
+note: the lint level is defined here
+ --> $DIR/match_type_on_diag_item.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
+
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:32:17
+ |
+LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)`
+
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+ --> $DIR/match_type_on_diag_item.rs:35:17
+ |
+LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
new file mode 100644
index 000000000..bb82faf0c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+declare_lint! {
+ pub TEST_LINT,
+ Warn,
+ ""
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+impl<'tcx> LateLintPass<'tcx> for Pass {
+ fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) {
+ let _ = expr.span.ctxt().outer_expn_data();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs
new file mode 100644
index 000000000..187d468b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
+#![feature(rustc_private)]
+
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+declare_lint! {
+ pub TEST_LINT,
+ Warn,
+ ""
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+impl<'tcx> LateLintPass<'tcx> for Pass {
+ fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) {
+ let _ = expr.span.ctxt().outer_expn().expn_data();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
new file mode 100644
index 000000000..afef69678
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
@@ -0,0 +1,15 @@
+error: usage of `outer_expn().expn_data()`
+ --> $DIR/outer_expn_data.rs:25:34
+ |
+LL | let _ = expr.span.ctxt().outer_expn().expn_data();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
+ |
+note: the lint level is defined here
+ --> $DIR/outer_expn_data.rs:3:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
new file mode 100644
index 000000000..6033d06e4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![feature(rustc_private)]
+#![deny(clippy::internal)]
+#![allow(
+ clippy::borrow_deref_ref,
+ clippy::unnecessary_operation,
+ unused_must_use,
+ clippy::missing_clippy_version_attribute
+)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::{Ident, Symbol};
+
+fn main() {
+ Symbol::intern("foo") == rustc_span::sym::clippy;
+ Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower;
+ Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper;
+ Ident::empty().name == rustc_span::sym::clippy;
+ rustc_span::sym::clippy == Ident::empty().name;
+}
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
new file mode 100644
index 000000000..1bb5d55f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![feature(rustc_private)]
+#![deny(clippy::internal)]
+#![allow(
+ clippy::borrow_deref_ref,
+ clippy::unnecessary_operation,
+ unused_must_use,
+ clippy::missing_clippy_version_attribute
+)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::{Ident, Symbol};
+
+fn main() {
+ Symbol::intern("foo").as_str() == "clippy";
+ Symbol::intern("foo").to_string() == "self";
+ Symbol::intern("foo").to_ident_string() != "Self";
+ &*Ident::empty().as_str() == "clippy";
+ "clippy" == Ident::empty().to_string();
+}
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
new file mode 100644
index 000000000..a1f507f33
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
@@ -0,0 +1,39 @@
+error: unnecessary `Symbol` to string conversion
+ --> $DIR/unnecessary_symbol_str.rs:16:5
+ |
+LL | Symbol::intern("foo").as_str() == "clippy";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
+ |
+note: the lint level is defined here
+ --> $DIR/unnecessary_symbol_str.rs:3:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
+
+error: unnecessary `Symbol` to string conversion
+ --> $DIR/unnecessary_symbol_str.rs:17:5
+ |
+LL | Symbol::intern("foo").to_string() == "self";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
+
+error: unnecessary `Symbol` to string conversion
+ --> $DIR/unnecessary_symbol_str.rs:18:5
+ |
+LL | Symbol::intern("foo").to_ident_string() != "Self";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
+
+error: unnecessary `Symbol` to string conversion
+ --> $DIR/unnecessary_symbol_str.rs:19:5
+ |
+LL | &*Ident::empty().as_str() == "clippy";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
+
+error: unnecessary `Symbol` to string conversion
+ --> $DIR/unnecessary_symbol_str.rs:20:5
+ |
+LL | "clippy" == Ident::empty().to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs b/src/tools/clippy/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs
new file mode 100644
index 000000000..195fabdbf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::arithmetic)]
+
+use core::ops::Add;
+
+#[derive(Clone, Copy)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+impl Add for Point {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ todo!()
+ }
+}
+
+fn main() {
+ let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
+
+ let point: Point = Point { x: 1, y: 0 };
+ let _ = point + point;
+}
diff --git a/src/tools/clippy/tests/ui-toml/arithmetic_allowed/clippy.toml b/src/tools/clippy/tests/ui-toml/arithmetic_allowed/clippy.toml
new file mode 100644
index 000000000..cc40570b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arithmetic_allowed/clippy.toml
@@ -0,0 +1 @@
+arithmetic-allowed = ["Point"]
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
new file mode 100644
index 000000000..fbef5c456
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
@@ -0,0 +1,41 @@
+#![warn(clippy::await_holding_invalid_type)]
+use std::net::Ipv4Addr;
+
+async fn bad() -> u32 {
+ let _x = String::from("hello");
+ baz().await
+}
+
+async fn bad_reason() -> u32 {
+ let _x = Ipv4Addr::new(127, 0, 0, 1);
+ baz().await
+}
+
+async fn good() -> u32 {
+ {
+ let _x = String::from("hi!");
+ let _y = Ipv4Addr::new(127, 0, 0, 1);
+ }
+ baz().await;
+ let _x = String::from("hi!");
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad() -> impl std::future::Future<Output = u32> {
+ async move {
+ let _x = String::from("hi!");
+ baz().await
+ }
+}
+
+fn main() {
+ good();
+ bad();
+ bad_reason();
+ block_bad();
+}
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
new file mode 100644
index 000000000..62c45b546
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
@@ -0,0 +1,25 @@
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:5:9
+ |
+LL | let _x = String::from("hello");
+ | ^^
+ |
+ = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
+ = note: strings are bad
+
+error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:10:9
+ |
+LL | let _x = Ipv4Addr::new(127, 0, 0, 1);
+ | ^^
+
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:31:13
+ |
+LL | let _x = String::from("hi!");
+ | ^^
+ |
+ = note: strings are bad
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml
new file mode 100644
index 000000000..79990096b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml
@@ -0,0 +1,4 @@
+await-holding-invalid-types = [
+ { path = "std::string::String", reason = "strings are bad" },
+ "std::net::Ipv4Addr",
+]
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml b/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml
new file mode 100644
index 000000000..823e01a33
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml
@@ -0,0 +1,2 @@
+fn this_is_obviously(not: a, toml: file) {
+}
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
new file mode 100644
index 000000000..28c1a568a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
@@ -0,0 +1,4 @@
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml b/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml
new file mode 100644
index 000000000..168675394
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml
@@ -0,0 +1 @@
+blacklisted-names = 42
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
new file mode 100644
index 000000000..c7bc261de
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
@@ -0,0 +1,4 @@
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs
new file mode 100644
index 000000000..fb2395cf9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs
@@ -0,0 +1,10 @@
+#[warn(clippy::blacklisted_name)]
+
+fn main() {
+ // `foo` is part of the default configuration
+ let foo = "bar";
+ // `ducks` was unrightfully blacklisted
+ let ducks = ["quack", "quack"];
+ // `fox` is okay
+ let fox = ["what", "does", "the", "fox", "say", "?"];
+}
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr
new file mode 100644
index 000000000..9169bb0e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr
@@ -0,0 +1,16 @@
+error: use of a blacklisted/placeholder name `foo`
+ --> $DIR/blacklisted_names.rs:5:9
+ |
+LL | let foo = "bar";
+ | ^^^
+ |
+ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
+
+error: use of a blacklisted/placeholder name `ducks`
+ --> $DIR/blacklisted_names.rs:7:9
+ |
+LL | let ducks = ["quack", "quack"];
+ | ^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_append/clippy.toml b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/clippy.toml
new file mode 100644
index 000000000..0e052ef50
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_append/clippy.toml
@@ -0,0 +1 @@
+blacklisted-names = ["ducks", ".."]
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs
new file mode 100644
index 000000000..fb2395cf9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs
@@ -0,0 +1,10 @@
+#[warn(clippy::blacklisted_name)]
+
+fn main() {
+ // `foo` is part of the default configuration
+ let foo = "bar";
+ // `ducks` was unrightfully blacklisted
+ let ducks = ["quack", "quack"];
+ // `fox` is okay
+ let fox = ["what", "does", "the", "fox", "say", "?"];
+}
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr
new file mode 100644
index 000000000..ec6f7f084
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr
@@ -0,0 +1,10 @@
+error: use of a blacklisted/placeholder name `ducks`
+ --> $DIR/blacklisted_names.rs:7:9
+ |
+LL | let ducks = ["quack", "quack"];
+ | ^^^^^
+ |
+ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/clippy.toml b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/clippy.toml
new file mode 100644
index 000000000..4582f1c06
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/blacklisted_names_replace/clippy.toml
@@ -0,0 +1 @@
+blacklisted-names = ["ducks"]
diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml
new file mode 100644
index 000000000..ac47b1950
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml
@@ -0,0 +1,6 @@
+# that one is an error
+cyclomatic-complexity-threshold = 42
+
+# that one is white-listed
+[third-party]
+clippy-feature = "nightly"
diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
new file mode 100644
index 000000000..90021a034
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
@@ -0,0 +1,4 @@
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/clippy.toml b/src/tools/clippy/tests/ui-toml/dbg_macro/clippy.toml
new file mode 100644
index 000000000..4296655a0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/dbg_macro/clippy.toml
@@ -0,0 +1 @@
+allow-dbg-in-tests = true
diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs
new file mode 100644
index 000000000..5d9ce18f6
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs
@@ -0,0 +1,39 @@
+// 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);
+}
+
+#[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-toml/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr
new file mode 100644
index 000000000..46efb86dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr
@@ -0,0 +1,102 @@
+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: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/clippy.toml b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/clippy.toml
new file mode 100644
index 000000000..daf327685
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/clippy.toml
@@ -0,0 +1 @@
+doc-valid-idents = ["ClipPy", ".."]
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs
new file mode 100644
index 000000000..327a592e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs
@@ -0,0 +1,12 @@
+#![warn(clippy::doc_markdown)]
+
+/// This is a special interface for ClipPy which doesn't require backticks
+fn allowed_name() {}
+
+/// OAuth and LaTeX are inside Clippy's default list.
+fn default_name() {}
+
+/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+fn unknown_name() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr
new file mode 100644
index 000000000..0f767c9b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr
@@ -0,0 +1,14 @@
+error: item in documentation is missing backticks
+ --> $DIR/doc_markdown.rs:9:5
+ |
+LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+ |
+LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/clippy.toml b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/clippy.toml
new file mode 100644
index 000000000..70bc477b0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/clippy.toml
@@ -0,0 +1 @@
+doc-valid-idents = ["ClipPy"]
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs
new file mode 100644
index 000000000..327a592e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs
@@ -0,0 +1,12 @@
+#![warn(clippy::doc_markdown)]
+
+/// This is a special interface for ClipPy which doesn't require backticks
+fn allowed_name() {}
+
+/// OAuth and LaTeX are inside Clippy's default list.
+fn default_name() {}
+
+/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+fn unknown_name() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr
new file mode 100644
index 000000000..e0613eb86
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr
@@ -0,0 +1,36 @@
+error: item in documentation is missing backticks
+ --> $DIR/doc_markdown.rs:6:5
+ |
+LL | /// OAuth and LaTeX are inside Clippy's default list.
+ | ^^^^^
+ |
+ = note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+ |
+LL | /// `OAuth` and LaTeX are inside Clippy's default list.
+ | ~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc_markdown.rs:6:15
+ |
+LL | /// OAuth and LaTeX are inside Clippy's default list.
+ | ^^^^^
+ |
+help: try
+ |
+LL | /// OAuth and `LaTeX` are inside Clippy's default list.
+ | ~~~~~~~
+
+error: item in documentation is missing backticks
+ --> $DIR/doc_markdown.rs:9:5
+ |
+LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted.
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/expect_used/clippy.toml b/src/tools/clippy/tests/ui-toml/expect_used/clippy.toml
new file mode 100644
index 000000000..6933b8164
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/expect_used/clippy.toml
@@ -0,0 +1 @@
+allow-expect-in-tests = true
diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
new file mode 100644
index 000000000..22dcd3ae9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
@@ -0,0 +1,29 @@
+// compile-flags: --test
+#![warn(clippy::expect_used)]
+
+fn expect_option() {
+ let opt = Some(0);
+ let _ = opt.expect("");
+}
+
+fn expect_result() {
+ let res: Result<u8, ()> = Ok(0);
+ let _ = res.expect("");
+}
+
+fn main() {
+ expect_option();
+ expect_result();
+}
+
+#[test]
+fn test_expect_option() {
+ let opt = Some(0);
+ let _ = opt.expect("");
+}
+
+#[test]
+fn test_expect_result() {
+ let res: Result<u8, ()> = Ok(0);
+ let _ = res.expect("");
+}
diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
new file mode 100644
index 000000000..9cb2199ed
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
@@ -0,0 +1,19 @@
+error: used `expect()` on `an Option` value
+ --> $DIR/expect_used.rs:6:13
+ |
+LL | let _ = opt.expect("");
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::expect-used` implied by `-D warnings`
+ = help: if this value is an `None`, it will panic
+
+error: used `expect()` on `a Result` value
+ --> $DIR/expect_used.rs:11:13
+ |
+LL | let _ = res.expect("");
+ | ^^^^^^^^^^^^^^
+ |
+ = help: if this value is an `Err`, it will panic
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml
new file mode 100644
index 000000000..022eec3e0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml
@@ -0,0 +1 @@
+max-fn-params-bools = 1
diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs
new file mode 100644
index 000000000..42897b389
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs
@@ -0,0 +1,6 @@
+#![warn(clippy::fn_params_excessive_bools)]
+
+fn f(_: bool) {}
+fn g(_: bool, _: bool) {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr
new file mode 100644
index 000000000..d05adc3d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr
@@ -0,0 +1,11 @@
+error: more than 1 bools in function parameters
+ --> $DIR/test.rs:4:1
+ |
+LL | fn g(_: bool, _: bool) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
+ = help: consider refactoring bools into two-variant enums
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml b/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml
new file mode 100644
index 000000000..951dbb523
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml
@@ -0,0 +1 @@
+too-many-lines-threshold = 1
diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs
new file mode 100644
index 000000000..4ac037854
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs
@@ -0,0 +1,60 @@
+#![warn(clippy::too_many_lines)]
+#![allow(clippy::let_unit_value)]
+
+// This function should be considered one line.
+fn many_comments_but_one_line_of_code() {
+ /* 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.");
+}
+
+// This should be considered two and a fail.
+fn too_many_lines() {
+ println!("This is bad.");
+ println!("This is bad.");
+}
+
+// This should only fail once (#7517).
+async fn async_too_many_lines() {
+ println!("This is bad.");
+ println!("This is bad.");
+}
+
+// This should fail only once, without failing on the closure.
+fn closure_too_many_lines() {
+ let _ = {
+ println!("This is bad.");
+ println!("This is bad.");
+ };
+}
+
+// This should be considered one line.
+#[rustfmt::skip]
+fn comment_starts_after_code() {
+ let _ = 5; /* closing comment. */ /*
+ this line shouldn't be counted theoretically.
+ */
+}
+
+// This should be considered one line.
+fn comment_after_code() {
+ let _ = 5; /* this line should get counted once. */
+}
+
+// This should fail since it is technically two lines.
+#[rustfmt::skip]
+fn comment_before_code() {
+ let _ = "test";
+ /* This comment extends to the front of
+ the code but this line should still count. */ let _ = 5;
+}
+
+// This should be considered one line.
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
new file mode 100644
index 000000000..dc255bdca
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
@@ -0,0 +1,43 @@
+error: this function has too many lines (2/1)
+ --> $DIR/test.rs:19:1
+ |
+LL | / fn too_many_lines() {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::too-many-lines` implied by `-D warnings`
+
+error: this function has too many lines (4/1)
+ --> $DIR/test.rs:25:1
+ |
+LL | / async fn async_too_many_lines() {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | }
+ | |_^
+
+error: this function has too many lines (4/1)
+ --> $DIR/test.rs:31:1
+ |
+LL | / fn closure_too_many_lines() {
+LL | | let _ = {
+LL | | println!("This is bad.");
+LL | | println!("This is bad.");
+LL | | };
+LL | | }
+ | |_^
+
+error: this function has too many lines (2/1)
+ --> $DIR/test.rs:53:1
+ |
+LL | / fn comment_before_code() {
+LL | | let _ = "test";
+LL | | /* This comment extends to the front of
+LL | | the code but this line should still count. */ let _ = 5;
+LL | | }
+ | |_^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml
new file mode 100644
index 000000000..a1dd6b2f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml
@@ -0,0 +1,3 @@
+# that one is white-listed
+[third-party]
+clippy-feature = "nightly"
diff --git a/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/clippy.toml b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/clippy.toml
new file mode 100644
index 000000000..088b12b2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/clippy.toml
@@ -0,0 +1 @@
+msrv = "invalid.version"
diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs
new file mode 100644
index 000000000..2ebf28645
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs
@@ -0,0 +1,3 @@
+#![allow(clippy::redundant_clone)]
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr
new file mode 100644
index 000000000..e9d8fd2e0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr
@@ -0,0 +1,4 @@
+error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml b/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml
new file mode 100644
index 000000000..ea34bf9fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml
@@ -0,0 +1 @@
+max-include-file-size = 600
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
new file mode 100644
index 000000000..f3dbb6ad1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::large_include_file)]
+
+// Good
+const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
+const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
+
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+// Bad
+const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
new file mode 100644
index 000000000..6a685a583
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
@@ -0,0 +1,21 @@
+error: attempted to include a large file
+ --> $DIR/large_include_file.rs:13:43
+ |
+LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::large-include-file` implied by `-D warnings`
+ = note: the configuration allows a maximum size of 600 bytes
+ = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: attempted to include a large file
+ --> $DIR/large_include_file.rs:14:35
+ |
+LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the configuration allows a maximum size of 600 bytes
+ = note: this error originates in the macro `include_str` (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-toml/large_include_file/too_big.txt b/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt
new file mode 100644
index 000000000..9829c46bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer. \ No newline at end of file
diff --git a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/clippy.toml b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/clippy.toml
new file mode 100644
index 000000000..6feaf7d5c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/clippy.toml
@@ -0,0 +1 @@
+unreadable-literal-lint-fractions = false \ No newline at end of file
diff --git a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs
new file mode 100644
index 000000000..2498672d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.rs
@@ -0,0 +1,23 @@
+#![allow(clippy::excessive_precision)]
+#[deny(clippy::unreadable_literal)]
+
+fn allow_inconsistent_digit_grouping() {
+ #![allow(clippy::inconsistent_digit_grouping)]
+ let _pass1 = 100_200_300.123456789;
+}
+
+fn main() {
+ allow_inconsistent_digit_grouping();
+
+ let _pass1 = 100_200_300.100_200_300;
+ let _pass2 = 1.123456789;
+ let _pass3 = 1.0;
+ let _pass4 = 10000.00001;
+ let _pass5 = 1.123456789e1;
+
+ // due to clippy::inconsistent-digit-grouping
+ let _fail1 = 100_200_300.123456789;
+
+ // fail due to the integer part
+ let _fail2 = 100200300.300200100;
+}
diff --git a/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr
new file mode 100644
index 000000000..be505bda4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/lint_decimal_readability/test.stderr
@@ -0,0 +1,10 @@
+error: digits grouped inconsistently by underscores
+ --> $DIR/test.rs:19:18
+ |
+LL | let _fail1 = 100_200_300.123456789;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789`
+ |
+ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml
new file mode 100644
index 000000000..78c7e63b4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml
@@ -0,0 +1 @@
+max-suggested-slice-pattern-length = 8
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
new file mode 100644
index 000000000..21849a14f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
@@ -0,0 +1,23 @@
+#![deny(clippy::index_refutable_slice)]
+
+fn below_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ // This would usually not be linted but is included now due to the
+ // index limit in the config file
+ println!("{}", slice[7]);
+ }
+}
+
+fn above_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ // This will not be linted as 8 is above the limit
+ println!("{}", slice[8]);
+ }
+}
+
+fn main() {
+ below_limit();
+ above_limit();
+}
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr
new file mode 100644
index 000000000..d319e65d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/index_refutable_slice.rs:5:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/index_refutable_slice.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_7);
+ | ~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/clippy.toml b/src/tools/clippy/tests/ui-toml/min_rust_version/clippy.toml
new file mode 100644
index 000000000..8e17d8074
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.0.0"
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
new file mode 100644
index 000000000..1e3ec123a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
@@ -0,0 +1,98 @@
+#![allow(clippy::redundant_clone, clippy::unnecessary_operation)]
+#![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)]
+
+use std::mem::{size_of, size_of_val};
+use std::ops::Deref;
+
+mod enums {
+ enum E {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+ }
+
+ // user forgot to remove the marker
+ #[non_exhaustive]
+ enum Ep {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+ }
+}
+
+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
+ };
+}
+
+fn manual_strip_msrv() {
+ let s = "hello, world!";
+ if s.starts_with("hello, ") {
+ assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ }
+}
+
+fn check_index_refutable_slice() {
+ // This shouldn't trigger `clippy::index_refutable_slice` as the suggestion
+ // would only be valid from 1.42.0 onward
+ let slice: Option<&[u32]> = Some(&[1]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+}
+
+fn map_clone_suggest_copied() {
+ // This should still trigger the lint but suggest `cloned()` instead of `copied()`
+ let _: Option<u64> = Some(&16).map(|b| *b);
+}
+
+fn borrow_as_ptr() {
+ let val = 1;
+ let _p = &val as *const i32;
+
+ let mut val_mut = 1;
+ let _p_mut = &mut val_mut as *mut i32;
+}
+
+fn manual_bits() {
+ size_of::<i8>() * 8;
+ size_of_val(&0u32) * 8;
+}
+
+fn main() {
+ option_as_ref_deref();
+ match_like_matches();
+ match_same_arms();
+ match_same_arms2();
+ manual_strip_msrv();
+ check_index_refutable_slice();
+ borrow_as_ptr();
+}
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr
new file mode 100644
index 000000000..5dae5af7e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr
@@ -0,0 +1,10 @@
+error: you are using an explicit closure for cloning elements
+ --> $DIR/min_rust_version.rs:74:26
+ |
+LL | let _: Option<u64> = Some(&16).map(|b| *b);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `Some(&16).cloned()`
+ |
+ = note: `-D clippy::map-clone` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml
new file mode 100644
index 000000000..05ba82287
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml
@@ -0,0 +1,10 @@
+enforced-import-renames = [
+ { path = "std::option::Option", rename = "Maybe" },
+ { path = "std::process::Child", rename = "Kid" },
+ { path = "std::process::exit", rename = "goodbye" },
+ { path = "std::collections::BTreeMap", rename = "Map" },
+ { path = "std::clone", rename = "foo" },
+ { path = "std::thread::sleep", rename = "thread_sleep" },
+ { path = "std::any::type_name", rename = "ident" },
+ { path = "std::sync::Mutex", rename = "StdMutie" }
+]
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
new file mode 100644
index 000000000..f60058c86
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::missing_enforced_import_renames)]
+
+use std::alloc as colla;
+use std::option::Option as Maybe;
+use std::process::{exit as wrong_exit, Child as Kid};
+use std::thread::sleep;
+#[rustfmt::skip]
+use std::{
+ any::{type_name, Any},
+ clone,
+ sync :: Mutex,
+};
+
+fn main() {
+ use std::collections::BTreeMap as OopsWrongRename;
+}
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
new file mode 100644
index 000000000..45de8fdff
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
@@ -0,0 +1,40 @@
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:5:20
+ |
+LL | use std::process::{exit as wrong_exit, Child as Kid};
+ | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye`
+ |
+ = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings`
+
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:6:1
+ |
+LL | use std::thread::sleep;
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep`
+
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:9:11
+ |
+LL | any::{type_name, Any},
+ | ^^^^^^^^^ help: try: `type_name as ident`
+
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:10:5
+ |
+LL | clone,
+ | ^^^^^ help: try: `clone as foo`
+
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:11:5
+ |
+LL | sync :: Mutex,
+ | ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie`
+
+error: this import should be renamed
+ --> $DIR/conf_missing_enforced_import_rename.rs:15:5
+ |
+LL | use std::collections::BTreeMap as OopsWrongRename;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
new file mode 100644
index 000000000..6452189a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/auxiliary/proc_macro_derive.rs
@@ -0,0 +1,18 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(DeriveSomething)]
+pub fn derive(_: TokenStream) -> TokenStream {
+ "fn _f() -> Vec<u8> { vec![] }".parse().unwrap()
+}
+
+#[proc_macro]
+pub fn foo_bar(_: TokenStream) -> TokenStream {
+ "fn issue_7422() { eprintln!(); }".parse().unwrap()
+}
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/clippy.toml b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/clippy.toml
new file mode 100644
index 000000000..bced8948a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/clippy.toml
@@ -0,0 +1,6 @@
+standard-macro-braces = [
+ { name = "quote", brace = "{" },
+ { name = "quote::quote", brace = "{" },
+ { name = "eprint", brace = "[" },
+ { name = "type_pos", brace = "[" },
+]
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
new file mode 100644
index 000000000..5b4adc868
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
@@ -0,0 +1,60 @@
+// aux-build:proc_macro_derive.rs
+
+#![warn(clippy::nonstandard_macro_braces)]
+
+extern crate proc_macro_derive;
+extern crate quote;
+
+use quote::quote;
+
+#[derive(proc_macro_derive::DeriveSomething)]
+pub struct S;
+
+proc_macro_derive::foo_bar!();
+
+#[rustfmt::skip]
+macro_rules! test {
+ () => {
+ vec!{0, 0, 0}
+ };
+}
+
+#[rustfmt::skip]
+macro_rules! test2 {
+ ($($arg:tt)*) => {
+ format_args!($($arg)*)
+ };
+}
+
+macro_rules! type_pos {
+ ($what:ty) => {
+ Vec<$what>
+ };
+}
+
+macro_rules! printlnfoo {
+ ($thing:expr) => {
+ println!("{}", $thing)
+ };
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _ = vec! {1, 2, 3};
+ let _ = format!["ugh {} stop being such a good compiler", "hello"];
+ let _ = quote!(let x = 1;);
+ let _ = quote::quote!(match match match);
+ let _ = test!(); // trigger when macro def is inside our own crate
+ let _ = vec![1,2,3];
+
+ let _ = quote::quote! {true || false};
+ let _ = vec! [0 ,0 ,0];
+ let _ = format!("fds{}fds", 10);
+ let _ = test2!["{}{}{}", 1, 2, 3];
+
+ let _: type_pos!(usize) = vec![];
+
+ eprint!("test if user config overrides defaults");
+
+ printlnfoo!["test if printlnfoo is triggered by println"];
+}
diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr
new file mode 100644
index 000000000..039b23b1b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr
@@ -0,0 +1,94 @@
+error: use of irregular braces for `vec!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:43:13
+ |
+LL | let _ = vec! {1, 2, 3};
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings`
+help: consider writing `vec![1, 2, 3]`
+ --> $DIR/conf_nonstandard_macro_braces.rs:43:13
+ |
+LL | let _ = vec! {1, 2, 3};
+ | ^^^^^^^^^^^^^^
+
+error: use of irregular braces for `format!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:44:13
+ |
+LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
+ --> $DIR/conf_nonstandard_macro_braces.rs:44:13
+ |
+LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of irregular braces for `quote!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:45:13
+ |
+LL | let _ = quote!(let x = 1;);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+help: consider writing `quote! {let x = 1;}`
+ --> $DIR/conf_nonstandard_macro_braces.rs:45:13
+ |
+LL | let _ = quote!(let x = 1;);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: use of irregular braces for `quote::quote!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:46:13
+ |
+LL | let _ = quote::quote!(match match match);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider writing `quote::quote! {match match match}`
+ --> $DIR/conf_nonstandard_macro_braces.rs:46:13
+ |
+LL | let _ = quote::quote!(match match match);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of irregular braces for `vec!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:18:9
+ |
+LL | vec!{0, 0, 0}
+ | ^^^^^^^^^^^^^
+...
+LL | let _ = test!(); // trigger when macro def is inside our own crate
+ | ------- in this macro invocation
+ |
+help: consider writing `vec![0, 0, 0]`
+ --> $DIR/conf_nonstandard_macro_braces.rs:18:9
+ |
+LL | vec!{0, 0, 0}
+ | ^^^^^^^^^^^^^
+...
+LL | let _ = test!(); // trigger when macro def is inside our own crate
+ | ------- in this macro invocation
+ = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: use of irregular braces for `type_pos!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:55:12
+ |
+LL | let _: type_pos!(usize) = vec![];
+ | ^^^^^^^^^^^^^^^^
+ |
+help: consider writing `type_pos![usize]`
+ --> $DIR/conf_nonstandard_macro_braces.rs:55:12
+ |
+LL | let _: type_pos!(usize) = vec![];
+ | ^^^^^^^^^^^^^^^^
+
+error: use of irregular braces for `eprint!` macro
+ --> $DIR/conf_nonstandard_macro_braces.rs:57:5
+ |
+LL | eprint!("test if user config overrides defaults");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider writing `eprint!["test if user config overrides defaults"]`
+ --> $DIR/conf_nonstandard_macro_braces.rs:57:5
+ |
+LL | eprint!("test if user config overrides defaults");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml
new file mode 100644
index 000000000..a942709d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml
@@ -0,0 +1 @@
+enable-raw-pointer-heuristic-for-send = false
diff --git a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs
new file mode 100644
index 000000000..90c2439dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs
@@ -0,0 +1,43 @@
+#![warn(clippy::non_send_fields_in_send_ty)]
+#![feature(extern_types)]
+
+use std::rc::Rc;
+
+// Basic tests should not be affected
+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> {}
+
+// All fields are disallowed when raw pointer heuristic is off
+extern "C" {
+ type NonSend;
+}
+
+pub struct HeuristicTest {
+ field1: Vec<*const NonSend>,
+ field2: [*const NonSend; 3],
+ field3: (*const NonSend, *const NonSend, *const NonSend),
+ field4: (*const NonSend, Rc<u8>),
+ field5: Vec<Vec<*const NonSend>>,
+}
+
+unsafe impl Send for HeuristicTest {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr
new file mode 100644
index 000000000..49eecf18b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr
@@ -0,0 +1,91 @@
+error: some fields in `NoGeneric` are not safe to be sent to another thread
+ --> $DIR/test.rs:11:1
+ |
+LL | unsafe impl Send for NoGeneric {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
+note: it is not safe to send field `rc_is_not_send` to another thread
+ --> $DIR/test.rs:8: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/test.rs:19:1
+ |
+LL | unsafe impl<T> Send for MultiField<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field1` to another thread
+ --> $DIR/test.rs:14: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/test.rs:15: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/test.rs:16: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/test.rs:26:1
+ |
+LL | unsafe impl<T> Send for MyOption<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `0` to another thread
+ --> $DIR/test.rs:22:12
+ |
+LL | MySome(T),
+ | ^
+ = help: add `T: Send` bound in `Send` impl
+
+error: some fields in `HeuristicTest` are not safe to be sent to another thread
+ --> $DIR/test.rs:41:1
+ |
+LL | unsafe impl Send for HeuristicTest {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: it is not safe to send field `field1` to another thread
+ --> $DIR/test.rs:34:5
+ |
+LL | field1: Vec<*const NonSend>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+note: it is not safe to send field `field2` to another thread
+ --> $DIR/test.rs:35:5
+ |
+LL | field2: [*const NonSend; 3],
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+note: it is not safe to send field `field3` to another thread
+ --> $DIR/test.rs:36:5
+ |
+LL | field3: (*const NonSend, *const NonSend, *const NonSend),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+note: it is not safe to send field `field4` to another thread
+ --> $DIR/test.rs:37:5
+ |
+LL | field4: (*const NonSend, Rc<u8>),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+note: it is not safe to send field `field5` to another thread
+ --> $DIR/test.rs:38:5
+ |
+LL | field5: Vec<Vec<*const NonSend>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: use a thread-safe type that implements `Send`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml
new file mode 100644
index 000000000..3912ab542
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml
@@ -0,0 +1 @@
+max-struct-bools = 0
diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs
new file mode 100644
index 000000000..32dd80246
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::struct_excessive_bools)]
+
+struct S {
+ a: bool,
+}
+
+struct Foo;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr
new file mode 100644
index 000000000..65861d10d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr
@@ -0,0 +1,13 @@
+error: more than 0 bools in a struct
+ --> $DIR/test.rs:3:1
+ |
+LL | / struct S {
+LL | | a: 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: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml
new file mode 100644
index 000000000..6abe5a3bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml
@@ -0,0 +1 @@
+blacklisted-names = ["toto", "tata", "titi"]
diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs
new file mode 100644
index 000000000..cb35d0e85
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs
@@ -0,0 +1,20 @@
+#![allow(dead_code)]
+#![allow(clippy::single_match)]
+#![allow(unused_variables)]
+#![warn(clippy::blacklisted_name)]
+
+fn test(toto: ()) {}
+
+fn main() {
+ let toto = 42;
+ let tata = 42;
+ let titi = 42;
+
+ let tatab = 42;
+ let tatatataic = 42;
+
+ match (42, Some(1337), Some(0)) {
+ (toto, Some(tata), titi @ Some(_)) => (),
+ _ => (),
+ }
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr
new file mode 100644
index 000000000..84ba77851
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr
@@ -0,0 +1,46 @@
+error: use of a blacklisted/placeholder name `toto`
+ --> $DIR/conf_french_blacklisted_name.rs:6:9
+ |
+LL | fn test(toto: ()) {}
+ | ^^^^
+ |
+ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
+
+error: use of a blacklisted/placeholder name `toto`
+ --> $DIR/conf_french_blacklisted_name.rs:9:9
+ |
+LL | let toto = 42;
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `tata`
+ --> $DIR/conf_french_blacklisted_name.rs:10:9
+ |
+LL | let tata = 42;
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `titi`
+ --> $DIR/conf_french_blacklisted_name.rs:11:9
+ |
+LL | let titi = 42;
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `toto`
+ --> $DIR/conf_french_blacklisted_name.rs:17:10
+ |
+LL | (toto, Some(tata), titi @ Some(_)) => (),
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `tata`
+ --> $DIR/conf_french_blacklisted_name.rs:17:21
+ |
+LL | (toto, Some(tata), titi @ Some(_)) => (),
+ | ^^^^
+
+error: use of a blacklisted/placeholder name `titi`
+ --> $DIR/conf_french_blacklisted_name.rs:17:28
+ |
+LL | (toto, Some(tata), titi @ Some(_)) => (),
+ | ^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
new file mode 100644
index 000000000..c902d2112
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
@@ -0,0 +1,10 @@
+disallowed-methods = [
+ # just a string is shorthand for path only
+ "std::iter::Iterator::sum",
+ "f32::clamp",
+ "slice::sort_unstable",
+ # can give path and reason with an inline table
+ { path = "regex::Regex::is_match", reason = "no matching allowed" },
+ # can use an inline table but omit reason
+ { path = "regex::Regex::new" },
+]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
new file mode 100644
index 000000000..3397fa1ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::disallowed_methods)]
+
+extern crate regex;
+use regex::Regex;
+
+fn main() {
+ let re = Regex::new(r"ab.*c").unwrap();
+ re.is_match("abc");
+
+ let mut a = vec![1, 2, 3, 4];
+ a.iter().sum::<i32>();
+
+ a.sort_unstable();
+
+ let _ = 2.0f32.clamp(3.0f32, 4.0f32);
+ let _ = 2.0f64.clamp(3.0f64, 4.0f64);
+
+ let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
+ let re = indirect(".").unwrap();
+
+ let in_call = Box::new(f32::clamp);
+ let in_method_call = ["^", "$"].into_iter().map(Regex::new);
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
new file mode 100644
index 000000000..5cbb56754
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
@@ -0,0 +1,54 @@
+error: use of a disallowed method `regex::Regex::new`
+ --> $DIR/conf_disallowed_methods.rs:7:14
+ |
+LL | let re = Regex::new(r"ab.*c").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::disallowed-methods` implied by `-D warnings`
+
+error: use of a disallowed method `regex::Regex::is_match`
+ --> $DIR/conf_disallowed_methods.rs:8:5
+ |
+LL | re.is_match("abc");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no matching allowed (from clippy.toml)
+
+error: use of a disallowed method `std::iter::Iterator::sum`
+ --> $DIR/conf_disallowed_methods.rs:11:5
+ |
+LL | a.iter().sum::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: use of a disallowed method `slice::sort_unstable`
+ --> $DIR/conf_disallowed_methods.rs:13:5
+ |
+LL | a.sort_unstable();
+ | ^^^^^^^^^^^^^^^^^
+
+error: use of a disallowed method `f32::clamp`
+ --> $DIR/conf_disallowed_methods.rs:15:13
+ |
+LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: use of a disallowed method `regex::Regex::new`
+ --> $DIR/conf_disallowed_methods.rs:18:61
+ |
+LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
+ | ^^^^^^^^^^
+
+error: use of a disallowed method `f32::clamp`
+ --> $DIR/conf_disallowed_methods.rs:21:28
+ |
+LL | let in_call = Box::new(f32::clamp);
+ | ^^^^^^^^^^
+
+error: use of a disallowed method `regex::Regex::new`
+ --> $DIR/conf_disallowed_methods.rs:22:53
+ |
+LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new);
+ | ^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml
new file mode 100644
index 000000000..6cb9e2ef9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml
@@ -0,0 +1,15 @@
+disallowed-types = [
+ "std::collections::HashMap",
+ "std::sync::atomic::AtomicU32",
+ "syn::TypePath",
+ "proc_macro2::Ident",
+ "std::thread::Thread",
+ "std::time::Instant",
+ "std::io::Read",
+ "std::primitive::usize",
+ "bool",
+ # can give path and reason with an inline table
+ { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
+ # can use an inline table but omit reason
+ { path = "std::net::TcpListener" },
+]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
new file mode 100644
index 000000000..7f28efd67
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
@@ -0,0 +1,42 @@
+#![warn(clippy::disallowed_types)]
+
+extern crate quote;
+extern crate syn;
+
+use std::sync as foo;
+use std::sync::atomic::AtomicU32;
+use std::time::Instant as Sneaky;
+
+struct HashMap;
+
+fn bad_return_type() -> fn() -> Sneaky {
+ todo!()
+}
+
+fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+
+fn trait_obj(_: &dyn std::io::Read) {}
+
+fn full_and_single_path_prim(_: usize, _: bool) {}
+
+fn const_generics<const C: usize>() {}
+
+struct GenArg<const U: usize>([u8; U]);
+
+static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
+
+fn ip(_: std::net::Ipv4Addr) {}
+
+fn listener(_: std::net::TcpListener) {}
+
+#[allow(clippy::diverging_sub_expression)]
+fn main() {
+ let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ let _ = Sneaky::now();
+ let _ = foo::atomic::AtomicU32::new(0);
+ static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+ let _ = syn::Ident::new("", todo!());
+ let _ = HashMap;
+ let _: usize = 64_usize;
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
new file mode 100644
index 000000000..e3ece799c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
@@ -0,0 +1,132 @@
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:7:1
+ |
+LL | use std::sync::atomic::AtomicU32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::disallowed-types` implied by `-D warnings`
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:8:1
+ |
+LL | use std::time::Instant as Sneaky;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:12:33
+ |
+LL | fn bad_return_type() -> fn() -> Sneaky {
+ | ^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:16:28
+ |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+ | ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:16:39
+ |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::io::Read` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:18:22
+ |
+LL | fn trait_obj(_: &dyn std::io::Read) {}
+ | ^^^^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:20:33
+ |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+ | ^^^^^
+
+error: `bool` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:20:43
+ |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+ | ^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:22:28
+ |
+LL | fn const_generics<const C: usize>() {}
+ | ^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:24:24
+ |
+LL | struct GenArg<const U: usize>([u8; U]);
+ | ^^^^^
+
+error: `std::net::Ipv4Addr` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:28:10
+ |
+LL | fn ip(_: std::net::Ipv4Addr) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no IPv4 allowed (from clippy.toml)
+
+error: `std::net::TcpListener` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:30:16
+ |
+LL | fn listener(_: std::net::TcpListener) {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:34:48
+ |
+LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:34:12
+ |
+LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:35:13
+ |
+LL | let _ = Sneaky::now();
+ | ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:36:13
+ |
+LL | let _ = foo::atomic::AtomicU32::new(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:37:17
+ |
+LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:37:48
+ |
+LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `syn::TypePath` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:38:43
+ |
+LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+ | ^^^^^^^^^^^^^
+
+error: `syn::Ident` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:39:13
+ |
+LL | let _ = syn::Ident::new("", todo!());
+ | ^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:41:12
+ |
+LL | let _: usize = 64_usize;
+ | ^^^^^
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml
new file mode 100644
index 000000000..3b96f1fd0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml
@@ -0,0 +1 @@
+trivial-copy-size-limit = 2
diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
new file mode 100644
index 000000000..fb0e226f3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
@@ -0,0 +1,20 @@
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![deny(clippy::trivially_copy_pass_by_ref)]
+
+#[derive(Copy, Clone)]
+struct Foo(u8);
+
+#[derive(Copy, Clone)]
+struct Bar(u32);
+
+fn good(a: &mut u32, b: u32, c: &Bar, d: &u32) {}
+
+fn bad(x: &u16, y: &Foo) {}
+
+fn main() {
+ let (mut a, b, c, d, x, y) = (0, 0, Bar(0), 0, 0, Foo(0));
+ good(&mut a, b, &c, &d);
+ bad(&x, &y);
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
new file mode 100644
index 000000000..b3ef5928e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
@@ -0,0 +1,20 @@
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+ --> $DIR/test.rs:14:11
+ |
+LL | fn bad(x: &u16, y: &Foo) {}
+ | ^^^^ help: consider passing by value instead: `u16`
+ |
+note: the lint level is defined here
+ --> $DIR/test.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/test.rs:14:20
+ |
+LL | fn bad(x: &u16, y: &Foo) {}
+ | ^^^^ help: consider passing by value instead: `Foo`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
new file mode 100644
index 000000000..554b87cc5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
@@ -0,0 +1,6 @@
+# that one is an error
+foobar = 42
+
+# that one is white-listed
+[third-party]
+clippy-feature = "nightly"
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
new file mode 100644
index 000000000..f328e4d9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
new file mode 100644
index 000000000..fe5139c47
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -0,0 +1,45 @@
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
+ allow-dbg-in-tests
+ allow-expect-in-tests
+ allow-unwrap-in-tests
+ allowed-scripts
+ arithmetic-allowed
+ array-size-threshold
+ avoid-breaking-exported-api
+ await-holding-invalid-types
+ blacklisted-names
+ cargo-ignore-publish
+ cognitive-complexity-threshold
+ cyclomatic-complexity-threshold
+ disallowed-methods
+ disallowed-types
+ doc-valid-idents
+ enable-raw-pointer-heuristic-for-send
+ enforced-import-renames
+ enum-variant-name-threshold
+ enum-variant-size-threshold
+ literal-representation-threshold
+ max-fn-params-bools
+ max-include-file-size
+ max-struct-bools
+ max-suggested-slice-pattern-length
+ max-trait-bounds
+ msrv
+ pass-by-value-size-limit
+ single-char-binding-names-threshold
+ standard-macro-braces
+ third-party
+ too-large-for-stack
+ too-many-arguments-threshold
+ too-many-lines-threshold
+ trivial-copy-size-limit
+ type-complexity-threshold
+ unreadable-literal-lint-fractions
+ upper-case-acronyms-aggressive
+ vec-box-size-threshold
+ verbose-bit-mask-threshold
+ warn-on-all-wildcard-imports
+ at line 5 column 1
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/clippy.toml b/src/tools/clippy/tests/ui-toml/unwrap_used/clippy.toml
new file mode 100644
index 000000000..154626ef4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/clippy.toml
@@ -0,0 +1 @@
+allow-unwrap-in-tests = true
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
new file mode 100644
index 000000000..0e82fb20e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
@@ -0,0 +1,73 @@
+// compile-flags: --test
+
+#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)]
+#![warn(clippy::unwrap_used)]
+#![deny(clippy::get_unwrap)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+struct GetFalsePositive {
+ arr: [u32; 3],
+}
+
+impl GetFalsePositive {
+ fn get(&self, pos: usize) -> Option<&u32> {
+ self.arr.get(pos)
+ }
+ fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
+ self.arr.get_mut(pos)
+ }
+}
+
+fn main() {
+ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let mut some_slice = &mut [0, 1, 2, 3];
+ let mut some_vec = vec![0, 1, 2, 3];
+ let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
+ let mut some_hashmap: HashMap<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();
+ }
+}
+
+#[test]
+fn test() {
+ let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+ let _ = boxed_slice.get(1).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
new file mode 100644
index 000000000..6bcfa0a8b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -0,0 +1,197 @@
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:35:17
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
+ |
+note: the lint level is defined here
+ --> $DIR/unwrap_used.rs:5:9
+ |
+LL | #![deny(clippy::get_unwrap)]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:35:17
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unwrap-used` implied by `-D warnings`
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:36:17
+ |
+LL | let _ = some_slice.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:36:17
+ |
+LL | let _ = some_slice.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:37:17
+ |
+LL | let _ = some_vec.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:37:17
+ |
+LL | let _ = some_vec.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:38:17
+ |
+LL | let _ = some_vecdeque.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:38:17
+ |
+LL | let _ = some_vecdeque.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:39:17
+ |
+LL | let _ = some_hashmap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:39:17
+ |
+LL | let _ = some_hashmap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:40:17
+ |
+LL | let _ = some_btreemap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:40:17
+ |
+LL | let _ = some_btreemap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:44:21
+ |
+LL | let _: u8 = *boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:44:22
+ |
+LL | let _: u8 = *boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:49:9
+ |
+LL | *boxed_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:49:10
+ |
+LL | *boxed_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:50:9
+ |
+LL | *some_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:50:10
+ |
+LL | *some_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:51:9
+ |
+LL | *some_vec.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:51:10
+ |
+LL | *some_vec.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:52:9
+ |
+LL | *some_vecdeque.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:52:10
+ |
+LL | *some_vecdeque.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:64:17
+ |
+LL | let _ = some_vec.get(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:64:17
+ |
+LL | let _ = some_vec.get(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:65:17
+ |
+LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+ --> $DIR/unwrap_used.rs:65:17
+ |
+LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+ --> $DIR/unwrap_used.rs:72:13
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
+
+error: aborting due to 27 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/update-all-references.sh b/src/tools/clippy/tests/ui-toml/update-all-references.sh
new file mode 100755
index 000000000..4391499a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/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-toml/upper_case_acronyms_aggressive/clippy.toml b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml
new file mode 100644
index 000000000..cc94ec53e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml
@@ -0,0 +1 @@
+upper-case-acronyms-aggressive = true
diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs
new file mode 100644
index 000000000..1a5cf1b19
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs
@@ -0,0 +1,44 @@
+#![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;
+
+// don't warn on public items
+pub struct MIXEDCapital;
+
+pub struct FULLCAPITAL;
+
+// enum variants should not be linted if the num is pub
+pub enum ParseError<T> {
+ FULLCAPITAL(u8),
+ MIXEDCapital(String),
+ Utf8(std::string::FromUtf8Error),
+ Parse(T, String),
+}
+
+// private, do lint here
+enum ParseErrorPrivate<T> {
+ WASD(u8),
+ WASDMixed(String),
+ Utf8(std::string::FromUtf8Error),
+ Parse(T, String),
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr
new file mode 100644
index 000000000..02f29bbef
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr
@@ -0,0 +1,82 @@
+error: name `HTTPResponse` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:3:8
+ |
+LL | struct HTTPResponse; // not linted by default, but with cfg option
+ | ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse`
+ |
+ = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
+
+error: name `NS` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:8:5
+ |
+LL | NS, // not linted
+ | ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns`
+
+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`
+
+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 `GCCLLVMSomething` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:21:8
+ |
+LL | struct GCCLLVMSomething;
+ | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething`
+
+error: name `WASD` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:38:5
+ |
+LL | WASD(u8),
+ | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd`
+
+error: name `WASDMixed` contains a capitalized acronym
+ --> $DIR/upper_case_acronyms.rs:39:5
+ |
+LL | WASDMixed(String),
+ | ^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `WasdMixed`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml b/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml
new file mode 100644
index 000000000..039ea47fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml
@@ -0,0 +1 @@
+vec-box-size-threshold = 4
diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs
new file mode 100644
index 000000000..bf04bee16
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs
@@ -0,0 +1,15 @@
+struct S {
+ x: u64,
+}
+
+struct C {
+ y: u16,
+}
+
+struct Foo(Vec<Box<u8>>);
+struct Bar(Vec<Box<u32>>);
+struct Baz(Vec<Box<(u32, u32)>>);
+struct BarBaz(Vec<Box<S>>);
+struct FooBarBaz(Vec<Box<C>>);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr
new file mode 100644
index 000000000..cf194de3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr
@@ -0,0 +1,22 @@
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/test.rs:9:12
+ |
+LL | struct Foo(Vec<Box<u8>>);
+ | ^^^^^^^^^^^^ help: try: `Vec<u8>`
+ |
+ = note: `-D clippy::vec-box` implied by `-D warnings`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/test.rs:10:12
+ |
+LL | struct Bar(Vec<Box<u32>>);
+ | ^^^^^^^^^^^^^ help: try: `Vec<u32>`
+
+error: `Vec<T>` is already on the heap, the boxing is unnecessary
+ --> $DIR/test.rs:13:18
+ |
+LL | struct FooBarBaz(Vec<Box<C>>);
+ | ^^^^^^^^^^^ help: try: `Vec<C>`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml b/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml
new file mode 100644
index 000000000..42a1067b9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml
@@ -0,0 +1 @@
+single-char-binding-names-threshold = 0
diff --git a/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs b/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs
new file mode 100644
index 000000000..22aaa242b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs
@@ -0,0 +1,3 @@
+#![warn(clippy::many_single_char_names)]
+
+fn main() {}
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
+