Auto merge of #137752 - flip1995:clippy-subtree-update, r=Manishearth

Clippy subtree update

? `@Manishearth`

Cargo.lock change because of Clippy version bump and rustc_tool_utils new release.

Fixes #137640

Would be nice, if we could this merged before nightly is being build, so that this ICE is fixed tomorrow.
This commit is contained in:
bors 2025-03-01 22:22:06 +00:00
commit 351686bcfd
2649 changed files with 35476 additions and 16197 deletions

View file

@ -358,7 +358,7 @@ dependencies = [
"cargo_metadata 0.18.1", "cargo_metadata 0.18.1",
"directories", "directories",
"rustc-build-sysroot", "rustc-build-sysroot",
"rustc_tools_util", "rustc_tools_util 0.4.0",
"rustc_version", "rustc_version",
"serde", "serde",
"serde_json", "serde_json",
@ -522,7 +522,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]] [[package]]
name = "clippy" name = "clippy"
version = "0.1.86" version = "0.1.87"
dependencies = [ dependencies = [
"anstream", "anstream",
"cargo_metadata 0.18.1", "cargo_metadata 0.18.1",
@ -539,7 +539,7 @@ dependencies = [
"quote", "quote",
"regex", "regex",
"rinja", "rinja",
"rustc_tools_util", "rustc_tools_util 0.4.2",
"serde", "serde",
"serde_json", "serde_json",
"syn 2.0.96", "syn 2.0.96",
@ -553,7 +553,7 @@ dependencies = [
[[package]] [[package]]
name = "clippy_config" name = "clippy_config"
version = "0.1.86" version = "0.1.87"
dependencies = [ dependencies = [
"clippy_utils", "clippy_utils",
"itertools", "itertools",
@ -578,7 +578,7 @@ dependencies = [
[[package]] [[package]]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.86" version = "0.1.87"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"cargo_metadata 0.18.1", "cargo_metadata 0.18.1",
@ -601,7 +601,7 @@ dependencies = [
[[package]] [[package]]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.86" version = "0.1.87"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"itertools", "itertools",
@ -4454,6 +4454,10 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d" checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d"
[[package]]
name = "rustc_tools_util"
version = "0.4.2"
[[package]] [[package]]
name = "rustc_trait_selection" name = "rustc_trait_selection"
version = "0.0.0" version = "0.0.0"

View file

@ -6,11 +6,53 @@ document.
## Unreleased / Beta / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master) [609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master)
## Rust 1.85
Current stable, released 2025-02-20
[View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster)
### New Lints
* Added [`repr_packed_without_abi`] to `suspicious`
[#13398](https://github.com/rust-lang/rust-clippy/pull/13398)
* Added [`as_pointer_underscore`] to `restriction`
[#13251](https://github.com/rust-lang/rust-clippy/pull/13251)
* Added [`doc_nested_refdefs`] to `suspicious`
[#13707](https://github.com/rust-lang/rust-clippy/pull/13707)
* Added [`literal_string_with_formatting_args`] to `nursery`
[#13410](https://github.com/rust-lang/rust-clippy/pull/13410)
* Added [`doc_include_without_cfg`] to `restriction`
[#13625](https://github.com/rust-lang/rust-clippy/pull/13625)
### Enhancements
* [`indexing_slicing`]: Can now be allowed in tests using the [`allow-indexing-slicing-in-tests`]
configuration
[#13854](https://github.com/rust-lang/rust-clippy/pull/13854)
* [`if_let_mutex`]: disable lint from Edition 2024 since
[if_let_rescope](https://github.com/rust-lang/rust/issues/131154) was stabilized
[#13695](https://github.com/rust-lang/rust-clippy/pull/13695)
* [`format_in_format_args`], [`recursive_format_impl`], [`to_string_in_format_args`],
[`uninlined_format_args`], [`unused_format_specs`]: Can now support 3rd party format macros
if they're marked with the `#[clippy::format_args]` attribute
[#9948](https://github.com/rust-lang/rust-clippy/pull/9948)
### ICE Fixes
* [`trait_duplication_in_bounds`]: fix ICE on duplicate type or constant bound
[#13722](https://github.com/rust-lang/rust-clippy/pull/13722)
### Others
* `clippy_utils` is now published to crates.io. Note that this crate is and will remain unstable.
[#13700](https://github.com/rust-lang/rust-clippy/pull/13700)
## Rust 1.84 ## Rust 1.84
Current stable, released 2025-01-09 Released 2025-01-09
[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster) [View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster)
@ -5530,6 +5572,7 @@ Released 2018-09-13
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg [`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg
[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
[`doc_link_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_code
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
@ -5549,6 +5592,7 @@ Released 2018-09-13
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes [`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute [`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
[`elidable_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#elidable_lifetime_names
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
[`empty_docs`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_docs [`empty_docs`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_docs
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
@ -5683,6 +5727,7 @@ Released 2018-09-13
[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub [`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module [`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module
@ -5742,6 +5787,7 @@ Released 2018-09-13
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
@ -5761,6 +5807,7 @@ Released 2018-09-13
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_midpoint`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
@ -5812,6 +5859,7 @@ Released 2018-09-13
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none [`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
[`mem_replace_option_with_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_some
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default [`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit [`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
[`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars [`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars
@ -5939,6 +5987,7 @@ Released 2018-09-13
[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr [`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
[`owned_cow`]: https://rust-lang.github.io/rust-clippy/master/index.html#owned_cow
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
@ -6067,6 +6116,7 @@ Released 2018-09-13
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
[`single_option_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_option_map
[`single_range_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_range_in_vec_init [`single_range_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_range_in_vec_init
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
@ -6143,6 +6193,7 @@ Released 2018-09-13
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_id_on_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_id_on_box [`type_id_on_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_id_on_box
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unbuffered_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#unbuffered_bytes
[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
[`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion [`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion
[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
@ -6161,6 +6212,7 @@ Released 2018-09-13
[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns [`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
[`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_clippy_cfg [`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_clippy_cfg
[`unnecessary_debug_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_debug_formatting
[`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
@ -6267,6 +6319,7 @@ Released 2018-09-13
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
[`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
@ -6275,6 +6328,7 @@ Released 2018-09-13
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for
[`allow-unwrap-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-consts
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
@ -6290,6 +6344,7 @@ Released 2018-09-13
[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
[`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros

View file

@ -1,7 +1,7 @@
[package] [package]
name = "clippy" name = "clippy"
# begin autogenerated version # begin autogenerated version
version = "0.1.86" version = "0.1.87"
# end autogenerated version # end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
@ -25,7 +25,7 @@ path = "src/driver.rs"
[dependencies] [dependencies]
clippy_config = { path = "clippy_config" } clippy_config = { path = "clippy_config" }
clippy_lints = { path = "clippy_lints" } clippy_lints = { path = "clippy_lints" }
rustc_tools_util = "0.4.0" rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
tempfile = { version = "3.3", optional = true } tempfile = { version = "3.3", optional = true }
termize = "0.1" termize = "0.1"
color-print = "0.3.4" color-print = "0.3.4"
@ -54,7 +54,7 @@ parking_lot = "0.12"
tokio = { version = "1", features = ["io-util"] } tokio = { version = "1", features = ["io-util"] }
[build-dependencies] [build-dependencies]
rustc_tools_util = "0.4.0" rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
[features] [features]
integration = ["tempfile"] integration = ["tempfile"]

View file

@ -169,7 +169,7 @@ from the lint to the code of the test file and compare that to the contents of a
Use `cargo bless` to automatically generate the `.fixed` file while running Use `cargo bless` to automatically generate the `.fixed` file while running
the tests. the tests.
[rustfix]: https://github.com/rust-lang/rustfix [rustfix]: https://github.com/rust-lang/cargo/tree/master/crates/rustfix
## Testing manually ## Testing manually
@ -460,7 +460,7 @@ pub struct ManualStrip {
impl ManualStrip { impl ManualStrip {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { msrv: conf.msrv.clone() } Self { msrv: conf.msrv }
} }
} }
``` ```
@ -469,24 +469,13 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass
using the `Msrv::meets` method. using the `Msrv::meets` method.
``` rust ``` rust
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { if !self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) {
return; return;
} }
``` ```
The project's MSRV can also be specified as an attribute, which overrides Early lint passes should instead use `MsrvStack` coupled with
the value from `clippy.toml`. This can be accounted for using the `extract_msrv_attr!()`
`extract_msrv_attr!(LintContext)` macro and passing
`LateContext`/`EarlyContext`.
```rust,ignore
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
...
}
extract_msrv_attr!(LateContext);
}
```
Once the `msrv` is added to the lint, a relevant test case should be added to Once the `msrv` is added to the lint, a relevant test case should be added to
the lint's test file, `tests/ui/manual_strip.rs` in this example. It should the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
@ -512,8 +501,16 @@ in `clippy_config/src/conf.rs`:
```rust ```rust
define_Conf! { define_Conf! {
/// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports #[lints(
(msrv: Option<String> = None), allow_attributes,
allow_attributes_without_reason,
..
<the newly added lint name>,
..
unused_trait_names,
use_self,
)]
msrv: Msrv = Msrv::default(),
... ...
} }
``` ```

View file

@ -75,7 +75,7 @@ or if you modify a test file to add a test case.
> _Note:_ This command may update more files than you intended. In that case > _Note:_ This command may update more files than you intended. In that case
> only commit the files you wanted to update. > only commit the files you wanted to update.
[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests [UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#ui-test-walkthrough
## `cargo dev` ## `cargo dev`

View file

@ -203,7 +203,7 @@ We'll talk about suggestions more in depth in a [later chapter](emitting_lints.m
Use `cargo bless` to automatically generate the `.fixed` file after running Use `cargo bless` to automatically generate the `.fixed` file after running
the tests. the tests.
[`rustfix`]: https://github.com/rust-lang/rustfix [`rustfix`]: https://github.com/rust-lang/cargo/tree/master/crates/rustfix
[`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html [`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
## Testing Manually ## Testing Manually

View file

@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) * [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
## `allow-expect-in-consts`
Whether `expect` should be allowed in code always evaluated at compile time
**Default Value:** `true`
---
**Affected lints:**
* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
## `allow-expect-in-tests` ## `allow-expect-in-tests`
Whether `expect` should be allowed in test functions or `#[cfg(test)]` Whether `expect` should be allowed in test functions or `#[cfg(test)]`
@ -108,7 +118,7 @@ Whether to allow `r#""#` when `r""` can be used
--- ---
**Affected lints:** **Affected lints:**
* [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) * [`needless_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes)
## `allow-panic-in-tests` ## `allow-panic-in-tests`
@ -164,6 +174,16 @@ default configuration of Clippy. By default, any configuration will replace the
* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params) * [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params)
## `allow-unwrap-in-consts`
Whether `unwrap` should be allowed in code always evaluated at compile time
**Default Value:** `true`
---
**Affected lints:**
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
## `allow-unwrap-in-tests` ## `allow-unwrap-in-tests`
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
@ -360,6 +380,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
* [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) * [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
* [`needless_pass_by_ref_mut`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut) * [`needless_pass_by_ref_mut`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut)
* [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) * [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
* [`owned_cow`](https://rust-lang.github.io/rust-clippy/master/index.html#owned_cow)
* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) * [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) * [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) * [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
@ -394,6 +415,16 @@ For internal testing only, ignores the current `publish` settings in the Cargo m
* [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) * [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata)
## `check-incompatible-msrv-in-tests`
Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
**Default Value:** `false`
---
**Affected lints:**
* [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv)
## `check-private-items` ## `check-private-items`
Whether to also run the listed lints on private items. Whether to also run the listed lints on private items.
@ -738,15 +769,21 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) * [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
* [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error)
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
* [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok)
* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) * [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals)
* [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) * [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
* [`manual_div_ceil`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil)
* [`manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten)
* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
* [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint)
* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
* [`manual_option_as_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice)
* [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison) * [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison)
* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) * [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
@ -761,6 +798,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) * [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
* [`map_with_unused_argument_over_ranges`](https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges) * [`map_with_unused_argument_over_ranges`](https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges)
* [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) * [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
* [`mem_replace_option_with_some`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_some)
* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
@ -770,6 +808,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
* [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity)
* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push)
* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)

View file

@ -1,7 +1,7 @@
[package] [package]
name = "clippy_config" name = "clippy_config"
# begin autogenerated version # begin autogenerated version
version = "0.1.86" version = "0.1.87"
# end autogenerated version # end autogenerated version
edition = "2024" edition = "2024"
publish = false publish = false

View file

@ -1,8 +1,8 @@
use crate::ClippyConfiguration; use crate::ClippyConfiguration;
use crate::types::{ use crate::types::{
DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour,
SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings,
SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
}; };
use clippy_utils::msrvs::Msrv; use clippy_utils::msrvs::Msrv;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -288,6 +288,9 @@ define_Conf! {
/// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
#[lints(dbg_macro)] #[lints(dbg_macro)]
allow_dbg_in_tests: bool = false, allow_dbg_in_tests: bool = false,
/// Whether `expect` should be allowed in code always evaluated at compile time
#[lints(expect_used)]
allow_expect_in_consts: bool = true,
/// Whether `expect` should be allowed in test functions or `#[cfg(test)]` /// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
#[lints(expect_used)] #[lints(expect_used)]
allow_expect_in_tests: bool = false, allow_expect_in_tests: bool = false,
@ -298,7 +301,7 @@ define_Conf! {
#[lints(uninlined_format_args)] #[lints(uninlined_format_args)]
allow_mixed_uninlined_format_args: bool = true, allow_mixed_uninlined_format_args: bool = true,
/// Whether to allow `r#""#` when `r""` can be used /// Whether to allow `r#""#` when `r""` can be used
#[lints(unnecessary_raw_string_hashes)] #[lints(needless_raw_string_hashes)]
allow_one_hash_in_raw_strings: bool = false, allow_one_hash_in_raw_strings: bool = false,
/// Whether `panic` should be allowed in test functions or `#[cfg(test)]` /// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
#[lints(panic)] #[lints(panic)]
@ -325,6 +328,9 @@ define_Conf! {
#[lints(renamed_function_params)] #[lints(renamed_function_params)]
allow_renamed_params_for: Vec<String> = allow_renamed_params_for: Vec<String> =
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(), DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(),
/// Whether `unwrap` should be allowed in code always evaluated at compile time
#[lints(unwrap_used)]
allow_unwrap_in_consts: bool = true,
/// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
#[lints(unwrap_used)] #[lints(unwrap_used)]
allow_unwrap_in_tests: bool = false, allow_unwrap_in_tests: bool = false,
@ -432,6 +438,7 @@ define_Conf! {
linkedlist, linkedlist,
needless_pass_by_ref_mut, needless_pass_by_ref_mut,
option_option, option_option,
owned_cow,
rc_buffer, rc_buffer,
rc_mutex, rc_mutex,
redundant_allocation, redundant_allocation,
@ -448,7 +455,7 @@ define_Conf! {
avoid_breaking_exported_api: bool = true, avoid_breaking_exported_api: bool = true,
/// The list of types which may not be held across an await point. /// The list of types which may not be held across an await point.
#[lints(await_holding_invalid_type)] #[lints(await_holding_invalid_type)]
await_holding_invalid_types: Vec<DisallowedPath> = Vec::new(), await_holding_invalid_types: Vec<DisallowedPathWithoutReplacement> = Vec::new(),
/// DEPRECATED LINT: BLACKLISTED_NAME. /// DEPRECATED LINT: BLACKLISTED_NAME.
/// ///
/// Use the Disallowed Names lint instead /// Use the Disallowed Names lint instead
@ -457,6 +464,9 @@ define_Conf! {
/// For internal testing only, ignores the current `publish` settings in the Cargo manifest. /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
#[lints(cargo_common_metadata)] #[lints(cargo_common_metadata)]
cargo_ignore_publish: bool = false, cargo_ignore_publish: bool = false,
/// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
#[lints(incompatible_msrv)]
check_incompatible_msrv_in_tests: bool = false,
/// Whether to also run the listed lints on private items. /// Whether to also run the listed lints on private items.
#[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
check_private_items: bool = false, check_private_items: bool = false,
@ -607,15 +617,21 @@ define_Conf! {
from_over_into, from_over_into,
if_then_some_else_none, if_then_some_else_none,
index_refutable_slice, index_refutable_slice,
io_other_error,
iter_kv_map, iter_kv_map,
legacy_numeric_constants, legacy_numeric_constants,
lines_filter_map_ok,
manual_bits, manual_bits,
manual_c_str_literals, manual_c_str_literals,
manual_clamp, manual_clamp,
manual_div_ceil,
manual_flatten,
manual_hash_one, manual_hash_one,
manual_is_ascii_check, manual_is_ascii_check,
manual_let_else, manual_let_else,
manual_midpoint,
manual_non_exhaustive, manual_non_exhaustive,
manual_option_as_slice,
manual_pattern_char_comparison, manual_pattern_char_comparison,
manual_range_contains, manual_range_contains,
manual_rem_euclid, manual_rem_euclid,
@ -630,6 +646,7 @@ define_Conf! {
map_unwrap_or, map_unwrap_or,
map_with_unused_argument_over_ranges, map_with_unused_argument_over_ranges,
match_like_matches_macro, match_like_matches_macro,
mem_replace_option_with_some,
mem_replace_with_default, mem_replace_with_default,
missing_const_for_fn, missing_const_for_fn,
needless_borrow, needless_borrow,
@ -639,6 +656,7 @@ define_Conf! {
ptr_as_ptr, ptr_as_ptr,
redundant_field_names, redundant_field_names,
redundant_static_lifetimes, redundant_static_lifetimes,
repeat_vec_with_capacity,
same_item_push, same_item_push,
seek_from_current, seek_from_current,
seek_rewind, seek_rewind,
@ -652,7 +670,7 @@ define_Conf! {
unused_trait_names, unused_trait_names,
use_self, use_self,
)] )]
msrv: Msrv = Msrv::empty(), msrv: Msrv = Msrv::default(),
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value. /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
#[lints(large_types_passed_by_value)] #[lints(large_types_passed_by_value)]
pass_by_value_size_limit: u64 = 256, pass_by_value_size_limit: u64 = 256,

View file

@ -1,6 +1,8 @@
use clippy_utils::def_path_def_ids; use clippy_utils::def_path_def_ids;
use rustc_errors::{Applicability, Diag};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
use serde::de::{self, Deserializer, Visitor}; use serde::de::{self, Deserializer, Visitor};
use serde::{Deserialize, Serialize, ser}; use serde::{Deserialize, Serialize, ser};
use std::collections::HashMap; use std::collections::HashMap;
@ -12,37 +14,99 @@ pub struct Rename {
pub rename: String, pub rename: String,
} }
#[derive(Debug, Deserialize)] pub type DisallowedPathWithoutReplacement = DisallowedPath<false>;
#[serde(untagged)]
pub enum DisallowedPath { #[derive(Debug, Serialize)]
Simple(String), pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
WithReason { path: String, reason: Option<String> }, path: String,
reason: Option<String>,
replacement: Option<String>,
} }
impl DisallowedPath { impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<REPLACEMENT_ALLOWED> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let enum_ = DisallowedPathEnum::deserialize(deserializer)?;
if !REPLACEMENT_ALLOWED && enum_.replacement().is_some() {
return Err(de::Error::custom("replacement not allowed for this configuration"));
}
Ok(Self {
path: enum_.path().to_owned(),
reason: enum_.reason().map(ToOwned::to_owned),
replacement: enum_.replacement().map(ToOwned::to_owned),
})
}
}
// `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just
// above. `DisallowedPathEnum` is not meant to be used outside of this file.
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
enum DisallowedPathEnum {
Simple(String),
WithReason {
path: String,
reason: Option<String>,
replacement: Option<String>,
},
}
impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
pub fn path(&self) -> &str {
&self.path
}
pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> {
move |diag| {
if let Some(replacement) = &self.replacement {
diag.span_suggestion(
span,
self.reason.as_ref().map_or_else(|| String::from("use"), Clone::clone),
replacement,
Applicability::MachineApplicable,
);
} else if let Some(reason) = &self.reason {
diag.note(reason.clone());
}
}
}
}
impl DisallowedPathEnum {
pub fn path(&self) -> &str { pub fn path(&self) -> &str {
let (Self::Simple(path) | Self::WithReason { path, .. }) = self; let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
path path
} }
pub fn reason(&self) -> Option<&str> { fn reason(&self) -> Option<&str> {
match &self { match &self {
Self::WithReason { reason, .. } => reason.as_deref(), Self::WithReason { reason, .. } => reason.as_deref(),
Self::Simple(_) => None, Self::Simple(_) => None,
} }
} }
fn replacement(&self) -> Option<&str> {
match &self {
Self::WithReason { replacement, .. } => replacement.as_deref(),
Self::Simple(_) => None,
}
}
} }
/// Creates a map of disallowed items to the reason they were disallowed. /// Creates a map of disallowed items to the reason they were disallowed.
pub fn create_disallowed_map( pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
disallowed: &'static [DisallowedPath], disallowed: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
) -> DefIdMap<(&'static str, Option<&'static str>)> { ) -> DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> {
disallowed disallowed
.iter() .iter()
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason())) .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
.flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason)))) .flat_map(|(name, path, disallowed_path)| {
def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
})
.collect() .collect()
} }
@ -436,7 +500,6 @@ macro_rules! unimplemented_serialize {
} }
unimplemented_serialize! { unimplemented_serialize! {
DisallowedPath,
Rename, Rename,
MacroMatcher, MacroMatcher,
} }

View file

@ -4,7 +4,8 @@ use std::process::Command;
/// # Panics /// # Panics
/// ///
/// Panics if unable to run the dogfood test /// Panics if unable to run the dogfood test
pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) { #[allow(clippy::fn_params_excessive_bools)]
pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) {
let mut cmd = Command::new("cargo"); let mut cmd = Command::new("cargo");
cmd.current_dir(clippy_project_root()) cmd.current_dir(clippy_project_root())
@ -25,6 +26,10 @@ pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
dogfood_args.push("--allow-staged"); dogfood_args.push("--allow-staged");
} }
if allow_no_vcs {
dogfood_args.push("--allow-no-vcs");
}
cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" ")); cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
exit_if_err(cmd.status()); exit_if_err(cmd.status());

View file

@ -290,8 +290,13 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
.filter_map(|entry| { .filter_map(|entry| {
let entry = entry.expect("failed to find tests"); let entry = entry.expect("failed to find tests");
let path = entry.path(); let path = entry.path();
if path.extension() != Some("rs".as_ref())
if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { || path
.components()
.nth_back(1)
.is_some_and(|c| c.as_os_str() == "syntax-error-recovery")
|| entry.file_name() == "ice-3891.rs"
{
None None
} else { } else {
Some(entry.into_path().into_os_string()) Some(entry.into_path().into_os_string())

View file

@ -2,7 +2,7 @@ use crate::utils::{cargo_clippy_path, exit_if_err};
use std::process::{self, Command}; use std::process::{self, Command};
use std::{env, fs}; use std::{env, fs};
pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) { pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>) {
let is_file = match fs::metadata(path) { let is_file = match fs::metadata(path) {
Ok(metadata) => metadata.is_file(), Ok(metadata) => metadata.is_file(),
Err(e) => { Err(e) => {
@ -17,7 +17,7 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
.args(["run", "--bin", "clippy-driver", "--"]) .args(["run", "--bin", "clippy-driver", "--"])
.args(["-L", "./target/debug"]) .args(["-L", "./target/debug"])
.args(["-Z", "no-codegen"]) .args(["-Z", "no-codegen"])
.args(["--edition", "2021"]) .args(["--edition", edition])
.arg(path) .arg(path)
.args(args) .args(args)
// Prevent rustc from creating `rustc-ice-*` files the console output is enough. // Prevent rustc from creating `rustc-ice-*` files the console output is enough.

View file

@ -17,7 +17,8 @@ fn main() {
fix, fix,
allow_dirty, allow_dirty,
allow_staged, allow_staged,
} => dogfood::dogfood(fix, allow_dirty, allow_staged), allow_no_vcs,
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
DevCommand::UpdateLints { print_only, check } => { DevCommand::UpdateLints { print_only, check } => {
if print_only { if print_only {
@ -34,7 +35,7 @@ fn main() {
category, category,
r#type, r#type,
msrv, msrv,
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) { } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) {
Ok(()) => update_lints::update(utils::UpdateMode::Change), Ok(()) => update_lints::update(utils::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {e}"), Err(e) => eprintln!("Unable to create lint: {e}"),
}, },
@ -53,7 +54,12 @@ fn main() {
setup::git_hook::install_hook(force_override); setup::git_hook::install_hook(force_override);
} }
}, },
SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name), SetupSubcommand::Toolchain {
standalone,
force,
release,
name,
} => setup::toolchain::create(standalone, force, release, &name),
SetupSubcommand::VscodeTasks { remove, force_override } => { SetupSubcommand::VscodeTasks { remove, force_override } => {
if remove { if remove {
setup::vscode::remove_tasks(); setup::vscode::remove_tasks();
@ -68,7 +74,7 @@ fn main() {
RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(), RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
}, },
DevCommand::Serve { port, lint } => serve::run(port, lint), DevCommand::Serve { port, lint } => serve::run(port, lint),
DevCommand::Lint { path, args } => lint::run(&path, args.iter()), DevCommand::Lint { path, edition, args } => lint::run(&path, &edition, args.iter()),
DevCommand::RenameLint { DevCommand::RenameLint {
old_name, old_name,
new_name, new_name,
@ -106,6 +112,9 @@ enum DevCommand {
#[arg(long, requires = "fix")] #[arg(long, requires = "fix")]
/// Fix code even if the working directory has staged changes /// Fix code even if the working directory has staged changes
allow_staged: bool, allow_staged: bool,
#[arg(long, requires = "fix")]
/// Fix code even if a VCS was not detected
allow_no_vcs: bool,
}, },
/// Run rustfmt on all projects and tests /// Run rustfmt on all projects and tests
Fmt { Fmt {
@ -138,9 +147,9 @@ enum DevCommand {
#[command(name = "new_lint")] #[command(name = "new_lint")]
/// Create a new lint and run `cargo dev update_lints` /// Create a new lint and run `cargo dev update_lints`
NewLint { NewLint {
#[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")] #[arg(short, long, conflicts_with = "type", default_value = "late")]
/// Specify whether the lint runs during the early or late pass /// Specify whether the lint runs during the early or late pass
pass: String, pass: new_lint::Pass,
#[arg( #[arg(
short, short,
long, long,
@ -206,6 +215,9 @@ enum DevCommand {
/// cargo dev lint file.rs -- -W clippy::pedantic {n} /// cargo dev lint file.rs -- -W clippy::pedantic {n}
/// cargo dev lint ~/my-project -- -- -W clippy::pedantic /// cargo dev lint ~/my-project -- -- -W clippy::pedantic
Lint { Lint {
/// The Rust edition to use
#[arg(long, default_value = "2024")]
edition: String,
/// The path to a file or package directory to lint /// The path to a file or package directory to lint
path: String, path: String,
/// Pass extra arguments to cargo/clippy-driver /// Pass extra arguments to cargo/clippy-driver
@ -264,14 +276,25 @@ enum SetupSubcommand {
force_override: bool, force_override: bool,
}, },
/// Install a rustup toolchain pointing to the local clippy build /// Install a rustup toolchain pointing to the local clippy build
///
/// This creates a toolchain with symlinks pointing at
/// `target/.../{clippy-driver,cargo-clippy}`, rebuilds of the project will be reflected in the
/// created toolchain unless `--standalone` is passed
Toolchain { Toolchain {
#[arg(long, short)]
/// Create a standalone toolchain by copying the clippy binaries instead
/// of symlinking them
///
/// Use this for example to create a toolchain, make a small change and then make another
/// toolchain with a different name in order to easily compare the two
standalone: bool,
#[arg(long, short)] #[arg(long, short)]
/// Override an existing toolchain /// Override an existing toolchain
force: bool, force: bool,
#[arg(long, short)] #[arg(long, short)]
/// Point to --release clippy binary /// Point to --release clippy binary
release: bool, release: bool,
#[arg(long, default_value = "clippy")] #[arg(long, short, default_value = "clippy")]
/// Name of the toolchain /// Name of the toolchain
name: String, name: String,
}, },

View file

@ -1,14 +1,28 @@
use crate::utils::{clippy_project_root, clippy_version}; use crate::utils::{clippy_project_root, clippy_version};
use clap::ValueEnum;
use indoc::{formatdoc, writedoc}; use indoc::{formatdoc, writedoc};
use std::fmt; use std::fmt::{self, Write as _};
use std::fmt::Write as _;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::prelude::*; use std::io::{self, Write as _};
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Clone, Copy, PartialEq, ValueEnum)]
pub enum Pass {
Early,
Late,
}
impl fmt::Display for Pass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Pass::Early => "early",
Pass::Late => "late",
})
}
}
struct LintData<'a> { struct LintData<'a> {
pass: &'a str, pass: Pass,
name: &'a str, name: &'a str,
category: &'a str, category: &'a str,
ty: Option<&'a str>, ty: Option<&'a str>,
@ -25,7 +39,7 @@ impl<T> Context for io::Result<T> {
Ok(t) => Ok(t), Ok(t) => Ok(t),
Err(e) => { Err(e) => {
let message = format!("{}: {e}", text.as_ref()); let message = format!("{}: {e}", text.as_ref());
Err(io::Error::new(ErrorKind::Other, message)) Err(io::Error::other(message))
}, },
} }
} }
@ -36,7 +50,7 @@ impl<T> Context for io::Result<T> {
/// # Errors /// # Errors
/// ///
/// This function errors out if the files couldn't be created or written to. /// This function errors out if the files couldn't be created or written to.
pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
if category == "cargo" && ty.is_none() { if category == "cargo" && ty.is_none() {
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
ty = Some("cargo"); ty = Some("cargo");
@ -57,7 +71,7 @@ pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?; add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
} }
if pass == "early" { if pass == Pass::Early {
println!( println!(
"\n\ "\n\
NOTE: Use a late pass unless you need something specific from\n\ NOTE: Use a late pass unless you need something specific from\n\
@ -137,23 +151,17 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
let mut lib_rs = fs::read_to_string(path).context("reading")?; let mut lib_rs = fs::read_to_string(path).context("reading")?;
let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment"); let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
let lint_pass = lint.pass;
let module_name = lint.name;
let camel_name = to_camel_case(lint.name);
let new_lint = if enable_msrv { let new_lint = if enable_msrv {
format!( format!(
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ", "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
lint_pass = lint.pass,
ctor_arg = if lint.pass == "late" { "_" } else { "" },
module_name = lint.name,
camel_name = to_camel_case(lint.name),
) )
} else { } else {
format!( format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",)
"store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",
lint_pass = lint.pass,
ctor_arg = if lint.pass == "late" { "_" } else { "" },
module_name = lint.name,
camel_name = to_camel_case(lint.name),
)
}; };
lib_rs.insert_str(comment_start, &new_lint); lib_rs.insert_str(comment_start, &new_lint);
@ -243,11 +251,16 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
let mut result = String::new(); let mut result = String::new();
let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
"early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), Pass::Early => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
"late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), Pass::Late => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
_ => { };
unreachable!("`pass_type` should only ever be `early` or `late`!"); let (msrv_ty, msrv_ctor, extract_msrv) = match lint.pass {
}, Pass::Early => (
"MsrvStack",
"MsrvStack::new(conf.msrv)",
"\n extract_msrv_attr!();\n",
),
Pass::Late => ("Msrv", "conf.msrv", ""),
}; };
let lint_name = lint.name; let lint_name = lint.name;
@ -259,10 +272,10 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
let _: fmt::Result = writedoc!( let _: fmt::Result = writedoc!(
result, result,
r" r"
use clippy_utils::msrvs::{{self, Msrv}}; use clippy_utils::msrvs::{{self, {msrv_ty}}};
use clippy_config::Conf; use clippy_config::Conf;
{pass_import} {pass_import}
use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_lint::{{{context_import}, {pass_type}}};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
" "
@ -286,20 +299,18 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
result, result,
r" r"
pub struct {name_camel} {{ pub struct {name_camel} {{
msrv: Msrv, msrv: {msrv_ty},
}} }}
impl {name_camel} {{ impl {name_camel} {{
pub fn new(conf: &'static Conf) -> Self {{ pub fn new(conf: &'static Conf) -> Self {{
Self {{ msrv: conf.msrv.clone() }} Self {{ msrv: {msrv_ctor} }}
}} }}
}} }}
impl_lint_pass!({name_camel} => [{name_upper}]); impl_lint_pass!({name_camel} => [{name_upper}]);
impl {pass_type}{pass_lifetimes} for {name_camel} {{ impl {pass_type}{pass_lifetimes} for {name_camel} {{{extract_msrv}}}
extract_msrv_attr!({context_import});
}}
// TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
// TODO: Update msrv config comment in `clippy_config/src/conf.rs` // TODO: Update msrv config comment in `clippy_config/src/conf.rs`
@ -376,9 +387,9 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
let mod_file_path = ty_dir.join("mod.rs"); let mod_file_path = ty_dir.join("mod.rs");
let context_import = setup_mod_file(&mod_file_path, lint)?; let context_import = setup_mod_file(&mod_file_path, lint)?;
let pass_lifetimes = match context_import { let (pass_lifetimes, msrv_ty, msrv_ref, msrv_cx) = match context_import {
"LateContext" => "<'_>", "LateContext" => ("<'_>", "Msrv", "", "cx, "),
_ => "", _ => ("", "MsrvStack", "&", ""),
}; };
let name_upper = lint.name.to_uppercase(); let name_upper = lint.name.to_uppercase();
@ -388,14 +399,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
let _: fmt::Result = writedoc!( let _: fmt::Result = writedoc!(
lint_file_contents, lint_file_contents,
r#" r#"
use clippy_utils::msrvs::{{self, Msrv}}; use clippy_utils::msrvs::{{self, {msrv_ty}}};
use rustc_lint::{{{context_import}, LintContext}}; use rustc_lint::{{{context_import}, LintContext}};
use super::{name_upper}; use super::{name_upper};
// TODO: Adjust the parameters as necessary // TODO: Adjust the parameters as necessary
pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: &Msrv) {{ pub(super) fn check(cx: &{context_import}{pass_lifetimes}, msrv: {msrv_ref}{msrv_ty}) {{
if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ if !msrv.meets({msrv_cx}todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
return; return;
}} }}
todo!(); todo!();

View file

@ -3,11 +3,14 @@ use std::env::current_dir;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::utils::exit_if_err;
use super::verify_inside_clippy_dir; use super::verify_inside_clippy_dir;
pub fn create(force: bool, release: bool, name: &str) { pub fn create(standalone: bool, force: bool, release: bool, name: &str) {
if !verify_inside_clippy_dir() { if !verify_inside_clippy_dir() {
return; return;
} }
@ -48,14 +51,22 @@ pub fn create(force: bool, release: bool, name: &str) {
} }
} }
symlink_bin("cargo-clippy", &dest, release); let status = Command::new("cargo")
symlink_bin("clippy-driver", &dest, release); .arg("build")
.args(release.then_some("--release"))
.status();
exit_if_err(status);
install_bin("cargo-clippy", &dest, standalone, release);
install_bin("clippy-driver", &dest, standalone, release);
println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`"); println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes"); if !standalone {
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
}
} }
fn symlink_bin(bin: &str, dest: &Path, release: bool) { fn install_bin(bin: &str, dest: &Path, standalone: bool, release: bool) {
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::fs::symlink_file as symlink; use std::os::windows::fs::symlink_file as symlink;
@ -71,5 +82,9 @@ fn symlink_bin(bin: &str, dest: &Path, release: bool) {
let mut dest = dest.to_path_buf(); let mut dest = dest.to_path_buf();
dest.extend(["bin", &file_name]); dest.extend(["bin", &file_name]);
symlink(src, dest).unwrap(); if standalone {
fs::copy(src, dest).unwrap();
} else {
symlink(src, dest).unwrap();
}
} }

View file

@ -1,7 +1,7 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
# begin autogenerated version # begin autogenerated version
version = "0.1.86" version = "0.1.87"
# end autogenerated version # end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"

View file

@ -1,6 +1,6 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, MsrvStack};
use clippy_utils::source::{trim_span, walk_span_to_context}; use clippy_utils::source::{trim_span, walk_span_to_context};
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -31,12 +31,12 @@ declare_clippy_lint! {
impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]);
pub struct AlmostCompleteRange { pub struct AlmostCompleteRange {
msrv: Msrv, msrv: MsrvStack,
} }
impl AlmostCompleteRange { impl AlmostCompleteRange {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: MsrvStack::new(conf.msrv),
} }
} }
} }
@ -96,7 +96,7 @@ impl EarlyLintPass for AlmostCompleteRange {
} }
} }
extract_msrv_attr!(EarlyContext); extract_msrv_attr!();
} }
fn is_incomplete_range(start: &Expr, end: &Expr) -> bool { fn is_incomplete_range(start: &Expr, end: &Expr) -> bool {

View file

@ -69,13 +69,11 @@ pub struct ApproxConstant {
impl ApproxConstant { impl ApproxConstant {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
impl<'tcx> LateLintPass<'tcx> for ApproxConstant { impl LateLintPass<'_> for ApproxConstant {
fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) { fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) {
match lit.node { match lit.node {
LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
@ -89,8 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
_ => (), _ => (),
} }
} }
extract_msrv_attr!(LateContext);
} }
impl ApproxConstant { impl ApproxConstant {
@ -98,7 +94,7 @@ impl ApproxConstant {
let s = s.as_str(); let s = s.as_str();
if s.parse::<f64>().is_ok() { if s.parse::<f64>().is_ok() {
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(msrv)) { if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(cx, msrv)) {
span_lint_and_help( span_lint_and_help(
cx, cx,
APPROX_CONSTANT, APPROX_CONSTANT,

View file

@ -9,12 +9,24 @@ declare_clippy_lint! {
/// Checks for usage of `as` conversions. /// Checks for usage of `as` conversions.
/// ///
/// Note that this lint is specialized in linting *every single* use of `as` /// Note that this lint is specialized in linting *every single* use of `as`
/// regardless of whether good alternatives exist or not. /// regardless of whether good alternatives exist or not. If you want more
/// If you want more precise lints for `as`, please consider using these separate lints: /// precise lints for `as`, please consider using these separate lints:
/// `unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`, ///
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. /// - clippy::cast_lossless
/// There is a good explanation the reason why this lint should work in this way and how it is useful /// - clippy::cast_possible_truncation
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). /// - clippy::cast_possible_wrap
/// - clippy::cast_precision_loss
/// - clippy::cast_sign_loss
/// - clippy::char_lit_as_u8
/// - clippy::fn_to_numeric_cast
/// - clippy::fn_to_numeric_cast_with_truncation
/// - clippy::ptr_as_ptr
/// - clippy::unnecessary_cast
/// - invalid_reference_casting
///
/// There is a good explanation the reason why this lint should work in this
/// way and how it is useful [in this
/// issue](https://github.com/rust-lang/rust-clippy/issues/5122).
/// ///
/// ### Why restrict this? /// ### Why restrict this?
/// `as` conversions will perform many kinds of /// `as` conversions will perform many kinds of

View file

@ -59,9 +59,7 @@ pub struct AssigningClones {
impl AssigningClones { impl AssigningClones {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -90,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone,
_ if fn_name.as_str() == "to_owned" _ if fn_name.as_str() == "to_owned"
&& is_diag_trait_item(cx, fn_id, sym::ToOwned) && is_diag_trait_item(cx, fn_id, sym::ToOwned)
&& self.msrv.meets(msrvs::CLONE_INTO) => && self.msrv.meets(cx, msrvs::CLONE_INTO) =>
{ {
CloneTrait::ToOwned CloneTrait::ToOwned
}, },
@ -143,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
/// Checks if the data being cloned borrows from the place that is being assigned to: /// Checks if the data being cloned borrows from the place that is being assigned to:

View file

@ -1,12 +1,12 @@
use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg}; use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, MsrvStack};
use rustc_ast::AttrStyle; use rustc_ast::AttrStyle;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::EarlyContext; use rustc_lint::EarlyContext;
use rustc_span::sym; use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
// check cfg_attr // check cfg_attr
if attr.has_name(sym::cfg_attr) if attr.has_name(sym::cfg_attr)
&& let Some(items) = attr.meta_item_list() && let Some(items) = attr.meta_item_list()

View file

@ -1,4 +1,3 @@
use std::sync::Arc;
use super::MIXED_ATTRIBUTES_STYLE; use super::MIXED_ATTRIBUTES_STYLE;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_ast::{AttrKind, AttrStyle, Attribute}; use rustc_ast::{AttrKind, AttrStyle, Attribute};
@ -6,6 +5,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{EarlyContext, LintContext}; use rustc_lint::{EarlyContext, LintContext};
use rustc_span::source_map::SourceMap; use rustc_span::source_map::SourceMap;
use rustc_span::{SourceFile, Span, Symbol}; use rustc_span::{SourceFile, Span, Symbol};
use std::sync::Arc;
#[derive(Hash, PartialEq, Eq)] #[derive(Hash, PartialEq, Eq)]
enum SimpleAttrKind { enum SimpleAttrKind {

View file

@ -14,7 +14,7 @@ mod useless_attribute;
mod utils; mod utils;
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv, MsrvStack};
use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
use rustc_hir::{ImplItem, Item, TraitItem}; use rustc_hir::{ImplItem, Item, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
@ -305,7 +305,7 @@ declare_clippy_lint! {
/// header_version: u16 /// header_version: u16
/// } /// }
/// ``` /// ```
#[clippy::version = "1.84.0"] #[clippy::version = "1.85.0"]
pub REPR_PACKED_WITHOUT_ABI, pub REPR_PACKED_WITHOUT_ABI,
suspicious, suspicious,
"ensures that `repr(packed)` always comes with a qualified ABI" "ensures that `repr(packed)` always comes with a qualified ABI"
@ -459,9 +459,7 @@ impl_lint_pass!(Attributes => [
impl Attributes { impl Attributes {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -471,7 +469,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
if is_relevant_item(cx, item) { if is_relevant_item(cx, item) {
inline_always::check(cx, item.span, item.ident.name, attrs); inline_always::check(cx, item.span, item.ident.name, attrs);
} }
repr_attributes::check(cx, item.span, attrs, &self.msrv); repr_attributes::check(cx, item.span, attrs, self.msrv);
} }
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@ -485,18 +483,16 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
} }
} }
extract_msrv_attr!(LateContext);
} }
pub struct EarlyAttributes { pub struct EarlyAttributes {
msrv: Msrv, msrv: MsrvStack,
} }
impl EarlyAttributes { impl EarlyAttributes {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: MsrvStack::new(conf.msrv),
} }
} }
} }
@ -515,17 +511,17 @@ impl EarlyLintPass for EarlyAttributes {
non_minimal_cfg::check(cx, attr); non_minimal_cfg::check(cx, attr);
} }
extract_msrv_attr!(EarlyContext); extract_msrv_attr!();
} }
pub struct PostExpansionEarlyAttributes { pub struct PostExpansionEarlyAttributes {
msrv: Msrv, msrv: MsrvStack,
} }
impl PostExpansionEarlyAttributes { impl PostExpansionEarlyAttributes {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: MsrvStack::new(conf.msrv),
} }
} }
} }
@ -589,5 +585,5 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
duplicated_attributes::check(cx, &item.attrs); duplicated_attributes::check(cx, &item.attrs);
} }
extract_msrv_attr!(EarlyContext); extract_msrv_attr!();
} }

View file

@ -1,33 +1,37 @@
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr}; use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs; use clippy_utils::msrvs::{self, Msrv};
use super::REPR_PACKED_WITHOUT_ABI; use super::REPR_PACKED_WITHOUT_ABI;
pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: &msrvs::Msrv) { pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: Msrv) {
if msrv.meets(msrvs::REPR_RUST) {
check_packed(cx, item_span, attrs);
}
}
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) { if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s); let packed_span = reprs
.iter()
.find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..)))
.map(|(_, s)| *s);
if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) { if let Some(packed_span) = packed_span
&& !reprs
.iter()
.any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust)
&& msrv.meets(cx, msrvs::REPR_RUST)
{
span_lint_and_then( span_lint_and_then(
cx, cx,
REPR_PACKED_WITHOUT_ABI, REPR_PACKED_WITHOUT_ABI,
item_span, item_span,
"item uses `packed` representation without ABI-qualification", "item uses `packed` representation without ABI-qualification",
|diag| { |diag| {
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI") diag.warn(
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") "unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI",
.span_label(packed_span, "`packed` representation set here"); )
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
.span_label(packed_span, "`packed` representation set here");
}, },
); );
} }

View file

@ -1,5 +1,5 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_config::types::create_disallowed_map; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{match_def_path, paths};
use rustc_hir as hir; use rustc_hir as hir;
@ -174,7 +174,7 @@ declare_clippy_lint! {
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
pub struct AwaitHolding { pub struct AwaitHolding {
def_ids: DefIdMap<(&'static str, Option<&'static str>)>, def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>,
} }
impl AwaitHolding { impl AwaitHolding {
@ -247,25 +247,26 @@ impl AwaitHolding {
); );
}, },
); );
} else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) { } else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) {
emit_invalid_type(cx, ty_cause.source_info.span, path, reason); emit_invalid_type(cx, ty_cause.source_info.span, path, disallowed_path);
} }
} }
} }
} }
} }
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, reason: Option<&'static str>) { fn emit_invalid_type(
cx: &LateContext<'_>,
span: Span,
path: &'static str,
disallowed_path: &'static DisallowedPathWithoutReplacement,
) {
span_lint_and_then( span_lint_and_then(
cx, cx,
AWAIT_HOLDING_INVALID_TYPE, AWAIT_HOLDING_INVALID_TYPE,
span, span,
format!("holding a disallowed type across an await point `{path}`"), format!("holding a disallowed type across an await point `{path}`"),
|diag| { disallowed_path.diag_amendment(span),
if let Some(reason) = reason {
diag.note(reason);
}
},
); );
} }

View file

@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value; use clippy_utils::eq_expr_value;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt; use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_attr_parsing::RustcVersion; use rustc_attr_parsing::RustcVersion;
@ -84,9 +85,7 @@ pub struct NonminimalBool {
impl NonminimalBool { impl NonminimalBool {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -102,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_: Span, _: Span,
_: LocalDefId, _: LocalDefId,
) { ) {
NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body); NonminimalBoolVisitor { cx, msrv: self.msrv }.visit_body(body);
} }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@ -119,8 +118,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_ => {}, _ => {},
} }
} }
extract_msrv_attr!(LateContext);
} }
fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> { fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
@ -197,7 +194,7 @@ fn check_inverted_bool_in_condition(
); );
} }
fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) { fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion() && !expr.span.from_expansion()
&& !inner.span.from_expansion() && !inner.span.from_expansion()
@ -233,7 +230,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
struct NonminimalBoolVisitor<'a, 'tcx> { struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
msrv: &'a Msrv, msrv: Msrv,
} }
use quine_mc_cluskey::Bool; use quine_mc_cluskey::Bool;
@ -326,7 +323,7 @@ impl<'v> Hir2Qmm<'_, '_, 'v> {
struct SuggestContext<'a, 'tcx, 'v> { struct SuggestContext<'a, 'tcx, 'v> {
terminals: &'v [&'v Expr<'v>], terminals: &'v [&'v Expr<'v>],
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
msrv: &'a Msrv, msrv: Msrv,
output: String, output: String,
} }
@ -353,7 +350,8 @@ impl SuggestContext<'_, '_, '_> {
self.output.push_str(&str); self.output.push_str(&str);
} else { } else {
self.output.push('!'); self.output.push('!');
self.output.push_str(&terminal.span.get_source_text(self.cx)?); self.output
.push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string());
} }
}, },
True | False | Not(_) => { True | False | Not(_) => {
@ -396,7 +394,7 @@ impl SuggestContext<'_, '_, '_> {
} }
} }
fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option<String> { fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Option<String> {
match &expr.kind { match &expr.kind {
ExprKind::Binary(binop, lhs, rhs) => { ExprKind::Binary(binop, lhs, rhs) => {
if !implements_ord(cx, lhs) { if !implements_ord(cx, lhs) {
@ -438,7 +436,9 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Opti
.iter() .iter()
.copied() .copied()
.flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)]) .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
.find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str()) .find(|&(msrv, a, _)| {
a == path.ident.name.as_str() && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv))
})
.and_then(|(_, _, neg_method)| { .and_then(|(_, _, neg_method)| {
let negated_args = args let negated_args = args
.iter() .iter()
@ -467,7 +467,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Opti
} }
} }
fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { fn suggest(cx: &LateContext<'_>, msrv: Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
let mut suggest_context = SuggestContext { let mut suggest_context = SuggestContext {
terminals, terminals,
cx, cx,
@ -553,7 +553,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
_ => simplified.push(Bool::Not(Box::new(simple.clone()))), _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
} }
let simple_negated = simple_negate(simple); let simple_negated = simple_negate(simple);
if simplified.iter().any(|s| *s == simple_negated) { if simplified.contains(&simple_negated) {
continue; continue;
} }
simplified.push(simple_negated); simplified.push(simple_negated);

View file

@ -16,7 +16,7 @@ pub(super) fn check<'tcx>(
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
cast_expr: &'tcx Expr<'_>, cast_expr: &'tcx Expr<'_>,
cast_to: &'tcx Ty<'_>, cast_to: &'tcx Ty<'_>,
msrv: &Msrv, msrv: Msrv,
) -> bool { ) -> bool {
if matches!(cast_to.kind, TyKind::Ptr(_)) if matches!(cast_to.kind, TyKind::Ptr(_))
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
return false; return false;
} }
let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) { let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) {
let operator_kind = match mutability { let operator_kind = match mutability {
Mutability::Not => "const", Mutability::Not => "const",
Mutability::Mut => "mut", Mutability::Mut => "mut",

View file

@ -14,13 +14,13 @@ pub(super) fn check(
cast_expr: &Expr<'_>, cast_expr: &Expr<'_>,
cast_from: Ty<'_>, cast_from: Ty<'_>,
cast_to: Ty<'_>, cast_to: Ty<'_>,
msrv: &Msrv, msrv: Msrv,
) { ) {
if msrv.meets(msrvs::UNSIGNED_ABS) if let ty::Int(from) = cast_from.kind()
&& let ty::Int(from) = cast_from.kind()
&& let ty::Uint(to) = cast_to.kind() && let ty::Uint(to) = cast_to.kind()
&& let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind
&& method_path.ident.name.as_str() == "abs" && method_path.ident.name.as_str() == "abs"
&& msrv.meets(cx, msrvs::UNSIGNED_ABS)
{ {
let span = if from.bit_width() == to.bit_width() { let span = if from.bit_width() == to.bit_width() {
expr.span expr.span

View file

@ -19,7 +19,7 @@ pub(super) fn check(
cast_from: Ty<'_>, cast_from: Ty<'_>,
cast_to: Ty<'_>, cast_to: Ty<'_>,
cast_to_hir: &rustc_hir::Ty<'_>, cast_to_hir: &rustc_hir::Ty<'_>,
msrv: &Msrv, msrv: Msrv,
) { ) {
if !should_lint(cx, cast_from, cast_to, msrv) { if !should_lint(cx, cast_from, cast_to, msrv) {
return; return;
@ -70,7 +70,7 @@ pub(super) fn check(
); );
} }
fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: Msrv) -> bool {
// Do not suggest using From in consts/statics until it is valid to do so (see #2267). // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
if is_in_const_context(cx) { if is_in_const_context(cx) {
return false; return false;
@ -96,7 +96,7 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &
}; };
!is_isize_or_usize(cast_from) && from_nbits < to_nbits !is_isize_or_usize(cast_from) && from_nbits < to_nbits
}, },
(false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true, (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(cx, msrvs::FROM_BOOL) => true,
(_, _) => { (_, _) => {
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
}, },

View file

@ -142,11 +142,11 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i
expr = recv; expr = recv;
} }
if METHODS_POW.iter().any(|&name| method_name == name) if METHODS_POW.contains(&method_name)
&& let [arg] = args && let [arg] = args
{ {
return pow_call_result_sign(cx, caller, arg); return pow_call_result_sign(cx, caller, arg);
} else if METHODS_RET_POSITIVE.iter().any(|&name| method_name == name) { } else if METHODS_RET_POSITIVE.contains(&method_name) {
return Sign::ZeroOrPositive; return Sign::ZeroOrPositive;
} }
} }

View file

@ -9,12 +9,7 @@ use rustc_middle::ty::{self, Ty, TypeAndMut};
use super::CAST_SLICE_DIFFERENT_SIZES; use super::CAST_SLICE_DIFFERENT_SIZES;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) {
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) {
return;
}
// if this cast is the child of another cast expression then don't emit something for it, the full // if this cast is the child of another cast expression then don't emit something for it, the full
// chain will be analyzed // chain will be analyzed
if is_child_of_cast(cx, expr) { if is_child_of_cast(cx, expr) {
@ -30,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
let from_size = from_layout.size.bytes(); let from_size = from_layout.size.bytes();
let to_size = to_layout.size.bytes(); let to_size = to_layout.size.bytes();
if from_size != to_size && from_size != 0 && to_size != 0 { if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
span_lint_and_then( span_lint_and_then(
cx, cx,
CAST_SLICE_DIFFERENT_SIZES, CAST_SLICE_DIFFERENT_SIZES,

View file

@ -23,9 +23,8 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
} }
} }
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: Msrv) {
if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) if let ty::RawPtr(ptrty, _) = cast_to.kind()
&& let ty::RawPtr(ptrty, _) = cast_to.kind()
&& let ty::Slice(_) = ptrty.kind() && let ty::Slice(_) = ptrty.kind()
&& let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind && let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind
&& let ExprKind::Path(ref qpath) = fun.kind && let ExprKind::Path(ref qpath) = fun.kind
@ -33,6 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&& let Some(rpk) = raw_parts_kind(cx, fun_def_id) && let Some(rpk) = raw_parts_kind(cx, fun_def_id)
&& let ctxt = expr.span.ctxt() && let ctxt = expr.span.ctxt()
&& cast_expr.span.ctxt() == ctxt && cast_expr.span.ctxt() == ctxt
&& msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS)
{ {
let func = match rpk { let func = match rpk {
RawPartsKind::Immutable => "from_raw_parts", RawPartsKind::Immutable => "from_raw_parts",

View file

@ -134,8 +134,14 @@ declare_clippy_lint! {
/// ///
/// ### Example /// ### Example
/// ```no_run /// ```no_run
/// u32::MAX as i32; // will yield a value of `-1` /// let _ = u32::MAX as i32; // will yield a value of `-1`
/// ``` /// ```
///
/// Use instead:
/// ```no_run
/// let _ = i32::try_from(u32::MAX).ok();
/// ```
///
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub CAST_POSSIBLE_WRAP, pub CAST_POSSIBLE_WRAP,
pedantic, pedantic,
@ -747,7 +753,7 @@ declare_clippy_lint! {
/// t as *const T as usize /// t as *const T as usize
/// } /// }
/// ``` /// ```
#[clippy::version = "1.81.0"] #[clippy::version = "1.85.0"]
pub AS_POINTER_UNDERSCORE, pub AS_POINTER_UNDERSCORE,
restriction, restriction,
"detects `as *mut _` and `as *const _` conversion" "detects `as *mut _` and `as *const _` conversion"
@ -759,9 +765,7 @@ pub struct Casts {
impl Casts { impl Casts {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -811,8 +815,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) { if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) {
return; return;
} }
cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, &self.msrv); cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv);
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
@ -825,29 +829,27 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_possible_wrap::check(cx, expr, cast_from, cast_to);
cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to);
cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to);
cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to);
} }
cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, &self.msrv); cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, self.msrv);
cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from); cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from);
} }
as_underscore::check(cx, expr, cast_to_hir); as_underscore::check(cx, expr, cast_to_hir);
as_pointer_underscore::check(cx, cast_to, cast_to_hir); as_pointer_underscore::check(cx, cast_to, cast_to_hir);
let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR) let was_borrow_as_ptr_emitted = self.msrv.meets(cx, msrvs::BORROW_AS_PTR)
&& borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv); && borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv);
if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { if !was_borrow_as_ptr_emitted && self.msrv.meets(cx, msrvs::PTR_FROM_REF) {
ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
} }
} }
cast_ptr_alignment::check(cx, expr); cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr); char_lit_as_u8::check(cx, expr);
ptr_as_ptr::check(cx, expr, &self.msrv); ptr_as_ptr::check(cx, expr, self.msrv);
cast_slice_different_sizes::check(cx, expr, &self.msrv); cast_slice_different_sizes::check(cx, expr, self.msrv);
ptr_cast_constness::check_null_ptr_cast_method(cx, expr); ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -26,11 +26,7 @@ impl OmitFollowedCastReason<'_> {
} }
} }
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {
if !msrv.meets(msrvs::POINTER_CAST) {
return;
}
if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
&& let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
&& let ty::RawPtr(_, from_mutbl) = cast_from.kind() && let ty::RawPtr(_, from_mutbl) = cast_from.kind()
@ -40,6 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
// The `U` in `pointer::cast` have to be `Sized` // The `U` in `pointer::cast` have to be `Sized`
// as explained here: https://github.com/rust-lang/rust/issues/60602. // as explained here: https://github.com/rust-lang/rust/issues/60602.
&& to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) && to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
&& msrv.meets(cx, msrvs::POINTER_CAST)
{ {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let turbofish = match &cast_to_hir_ty.kind { let turbofish = match &cast_to_hir_ty.kind {

View file

@ -16,7 +16,7 @@ pub(super) fn check<'tcx>(
cast_expr: &Expr<'_>, cast_expr: &Expr<'_>,
cast_from: Ty<'tcx>, cast_from: Ty<'tcx>,
cast_to: Ty<'tcx>, cast_to: Ty<'tcx>,
msrv: &Msrv, msrv: Msrv,
) { ) {
if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
&& let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
@ -52,7 +52,7 @@ pub(super) fn check<'tcx>(
return; return;
} }
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) { if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) {
let sugg = Sugg::hir(cx, cast_expr, "_"); let sugg = Sugg::hir(cx, cast_expr, "_");
let constness = match *to_mutbl { let constness = match *to_mutbl {
Mutability::Not => "const", Mutability::Not => "const",

View file

@ -39,9 +39,7 @@ pub struct CheckedConversions {
impl CheckedConversions { impl CheckedConversions {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -65,7 +63,6 @@ impl LateLintPass<'_> for CheckedConversions {
} }
&& !item.span.in_external_macro(cx.sess().source_map()) && !item.span.in_external_macro(cx.sess().source_map())
&& !is_in_const_context(cx) && !is_in_const_context(cx)
&& self.msrv.meets(msrvs::TRY_FROM)
&& let Some(cv) = match op2 { && let Some(cv) = match op2 {
// todo: check for case signed -> larger unsigned == only x >= 0 // todo: check for case signed -> larger unsigned == only x >= 0
None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned), None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned),
@ -79,6 +76,7 @@ impl LateLintPass<'_> for CheckedConversions {
}, },
} }
&& let Some(to_type) = cv.to_type && let Some(to_type) = cv.to_type
&& self.msrv.meets(cx, msrvs::TRY_FROM)
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
@ -93,8 +91,6 @@ impl LateLintPass<'_> for CheckedConversions {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
/// Contains the result of a tried conversion check /// Contains the result of a tried conversion check

View file

@ -18,7 +18,6 @@ use rustc_session::impl_lint_pass;
use rustc_span::hygiene::walk_chain; use rustc_span::hygiene::walk_chain;
use rustc_span::source_map::SourceMap; use rustc_span::source_map::SourceMap;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use std::borrow::Cow;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -130,11 +129,6 @@ declare_clippy_lint! {
/// ### Why is this bad? /// ### Why is this bad?
/// Duplicate code is less maintainable. /// Duplicate code is less maintainable.
/// ///
/// ### Known problems
/// * The lint doesn't check if the moved expressions modify values that are being used in
/// the if condition. The suggestion can in that case modify the behavior of the program.
/// See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
///
/// ### Example /// ### Example
/// ```ignore /// ```ignore
/// let foo = if … { /// let foo = if … {
@ -248,18 +242,18 @@ fn lint_branches_sharing_code<'tcx>(
let first_line_span = first_line_of_span(cx, expr.span); let first_line_span = first_line_of_span(cx, expr.span);
let replace_span = first_line_span.with_hi(span.hi()); let replace_span = first_line_span.with_hi(span.hi());
let cond_span = first_line_span.until(first_block.span); let cond_span = first_line_span.until(first_block.span);
let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None);
let cond_indent = indent_of(cx, cond_span); let cond_indent = indent_of(cx, cond_span);
let moved_snippet = reindent_multiline(snippet(cx, span, "_"), true, None); let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None);
let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); let suggestion = reindent_multiline(&suggestion, true, cond_indent);
(replace_span, suggestion.to_string()) (replace_span, suggestion.to_string())
}); });
let end_suggestion = res.end_span(last_block, sm).map(|span| { let end_suggestion = res.end_span(last_block, sm).map(|span| {
let moved_snipped = reindent_multiline(snippet(cx, span, "_"), true, None); let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None);
let indent = indent_of(cx, expr.span.shrink_to_hi()); let indent = indent_of(cx, expr.span.shrink_to_hi());
let suggestion = "}\n".to_string() + &moved_snipped; let suggestion = "}\n".to_string() + &moved_snipped;
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); let suggestion = reindent_multiline(&suggestion, true, indent);
let span = span.with_hi(last_block.span.hi()); let span = span.with_hi(last_block.span.hi());
// Improve formatting if the inner block has indention (i.e. normal Rust formatting) // Improve formatting if the inner block has indention (i.e. normal Rust formatting)

View file

@ -139,6 +139,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::disallowed_types::DISALLOWED_TYPES_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO,
crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LAZY_CONTINUATION_INFO,
crate::doc::DOC_LINK_CODE_INFO,
crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO,
crate::doc::DOC_MARKDOWN_INFO, crate::doc::DOC_MARKDOWN_INFO,
crate::doc::DOC_NESTED_REFDEFS_INFO, crate::doc::DOC_NESTED_REFDEFS_INFO,
@ -192,6 +193,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
crate::format_args::UNINLINED_FORMAT_ARGS_INFO, crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO,
crate::format_args::UNUSED_FORMAT_SPECS_INFO, crate::format_args::UNUSED_FORMAT_SPECS_INFO,
crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO,
crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO,
@ -272,6 +274,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO, crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO, crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO,
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
crate::lifetimes::NEEDLESS_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO, crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,
@ -362,6 +365,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO, crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
crate::matches::WILDCARD_IN_OR_PATTERNS_INFO, crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO, crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
crate::mem_replace::MEM_REPLACE_OPTION_WITH_SOME_INFO,
crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO,
crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO,
crate::methods::BIND_INSTEAD_OF_MAP_INFO, crate::methods::BIND_INSTEAD_OF_MAP_INFO,
@ -398,6 +402,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::INEFFICIENT_TO_STRING_INFO, crate::methods::INEFFICIENT_TO_STRING_INFO,
crate::methods::INSPECT_FOR_EACH_INFO, crate::methods::INSPECT_FOR_EACH_INFO,
crate::methods::INTO_ITER_ON_REF_INFO, crate::methods::INTO_ITER_ON_REF_INFO,
crate::methods::IO_OTHER_ERROR_INFO,
crate::methods::IS_DIGIT_ASCII_RADIX_INFO, crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
crate::methods::ITER_CLONED_COLLECT_INFO, crate::methods::ITER_CLONED_COLLECT_INFO,
@ -416,6 +421,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_SKIP_ZERO_INFO,
crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::ITER_WITH_DRAIN_INFO,
crate::methods::JOIN_ABSOLUTE_PATHS_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
crate::methods::MANUAL_CONTAINS_INFO,
crate::methods::MANUAL_C_STR_LITERALS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO,
@ -452,7 +458,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::OPTION_AS_REF_CLONED_INFO, crate::methods::OPTION_AS_REF_CLONED_INFO,
crate::methods::OPTION_AS_REF_DEREF_INFO, crate::methods::OPTION_AS_REF_DEREF_INFO,
crate::methods::OPTION_FILTER_MAP_INFO, crate::methods::OPTION_FILTER_MAP_INFO,
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
crate::methods::OPTION_MAP_OR_NONE_INFO, crate::methods::OPTION_MAP_OR_NONE_INFO,
crate::methods::OR_FUN_CALL_INFO, crate::methods::OR_FUN_CALL_INFO,
crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::OR_THEN_UNWRAP_INFO,
@ -483,6 +488,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO,
crate::methods::SUSPICIOUS_TO_OWNED_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO,
crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::TYPE_ID_ON_BOX_INFO,
crate::methods::UNBUFFERED_BYTES_INFO,
crate::methods::UNINIT_ASSUMED_INIT_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO,
crate::methods::UNIT_HASH_INFO, crate::methods::UNIT_HASH_INFO,
crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO, crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
@ -602,6 +608,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_DIVISION_INFO, crate::operators::INTEGER_DIVISION_INFO,
crate::operators::MANUAL_MIDPOINT_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
crate::operators::MODULO_ARITHMETIC_INFO, crate::operators::MODULO_ARITHMETIC_INFO,
crate::operators::MODULO_ONE_INFO, crate::operators::MODULO_ONE_INFO,
@ -684,6 +691,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::single_call_fn::SINGLE_CALL_FN_INFO, crate::single_call_fn::SINGLE_CALL_FN_INFO,
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
crate::single_option_map::SINGLE_OPTION_MAP_INFO,
crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO, crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO,
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
crate::size_of_ref::SIZE_OF_REF_INFO, crate::size_of_ref::SIZE_OF_REF_INFO,
@ -741,6 +749,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::types::BOX_COLLECTION_INFO, crate::types::BOX_COLLECTION_INFO,
crate::types::LINKEDLIST_INFO, crate::types::LINKEDLIST_INFO,
crate::types::OPTION_OPTION_INFO, crate::types::OPTION_OPTION_INFO,
crate::types::OWNED_COW_INFO,
crate::types::RC_BUFFER_INFO, crate::types::RC_BUFFER_INFO,
crate::types::RC_MUTEX_INFO, crate::types::RC_MUTEX_INFO,
crate::types::REDUNDANT_ALLOCATION_INFO, crate::types::REDUNDANT_ALLOCATION_INFO,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr}; use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
use rustc_hir::{HirId, Item, ItemKind}; use rustc_hir::{HirId, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;

View file

@ -40,6 +40,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"),
#[clippy::version = "1.54.0"] #[clippy::version = "1.54.0"]
("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"),
#[clippy::version = "1.86.0"]
("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"),
// end deprecated lints. used by `cargo dev deprecate_lint` // end deprecated lints. used by `cargo dev deprecate_lint`
]} ]}

View file

@ -62,9 +62,7 @@ pub struct DerivableImpls {
impl DerivableImpls { impl DerivableImpls {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
DerivableImpls { DerivableImpls { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -205,11 +203,9 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
{ {
if adt_def.is_struct() { if adt_def.is_struct() {
check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { } else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) {
check_enum(cx, item, func_expr, adt_def); check_enum(cx, item, func_expr, adt_def);
} }
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -1,10 +1,9 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_config::types::create_disallowed_map; use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace; use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Diag;
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_hir::{ use rustc_hir::{
AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
@ -60,7 +59,7 @@ declare_clippy_lint! {
} }
pub struct DisallowedMacros { pub struct DisallowedMacros {
disallowed: DefIdMap<(&'static str, Option<&'static str>)>, disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
seen: FxHashSet<ExpnId>, seen: FxHashSet<ExpnId>,
// Track the most recently seen node that can have a `derive` attribute. // Track the most recently seen node that can have a `derive` attribute.
// Needed to use the correct lint level. // Needed to use the correct lint level.
@ -91,13 +90,9 @@ impl DisallowedMacros {
return; return;
} }
if let Some(&(path, reason)) = self.disallowed.get(&mac.def_id) { if let Some(&(path, disallowed_path)) = self.disallowed.get(&mac.def_id) {
let msg = format!("use of a disallowed macro `{path}`"); let msg = format!("use of a disallowed macro `{path}`");
let add_note = |diag: &mut Diag<'_, _>| { let add_note = disallowed_path.diag_amendment(mac.span);
if let Some(reason) = reason {
diag.note(reason);
}
};
if matches!(mac.kind, MacroKind::Derive) if matches!(mac.kind, MacroKind::Derive)
&& let Some(derive_src) = derive_src && let Some(derive_src) = derive_src
{ {

View file

@ -1,5 +1,5 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_config::types::create_disallowed_map; use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
@ -31,6 +31,8 @@ declare_clippy_lint! {
/// # When using an inline table, can add a `reason` for why the method /// # When using an inline table, can add a `reason` for why the method
/// # is disallowed. /// # is disallowed.
/// { path = "std::vec::Vec::leak", reason = "no leaking memory" }, /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
/// # Can also add a `replacement` that will be offered as a suggestion.
/// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" },
/// ] /// ]
/// ``` /// ```
/// ///
@ -56,7 +58,7 @@ declare_clippy_lint! {
} }
pub struct DisallowedMethods { pub struct DisallowedMethods {
disallowed: DefIdMap<(&'static str, Option<&'static str>)>, disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
} }
impl DisallowedMethods { impl DisallowedMethods {
@ -83,17 +85,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
}, },
_ => return, _ => return,
}; };
if let Some(&(path, reason)) = self.disallowed.get(&id) { if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) {
span_lint_and_then( span_lint_and_then(
cx, cx,
DISALLOWED_METHODS, DISALLOWED_METHODS,
span, span,
format!("use of a disallowed method `{path}`"), format!("use of a disallowed method `{path}`"),
|diag| { disallowed_path.diag_amendment(span),
if let Some(reason) = reason {
diag.note(reason);
}
},
); );
} }
} }

View file

@ -1,4 +1,5 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Res; use rustc_hir::def::Res;
@ -31,6 +32,8 @@ declare_clippy_lint! {
/// # When using an inline table, can add a `reason` for why the type /// # When using an inline table, can add a `reason` for why the type
/// # is disallowed. /// # is disallowed.
/// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
/// # Can also add a `replacement` that will be offered as a suggestion.
/// { path = "std::sync::Mutex", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex" },
/// ] /// ]
/// ``` /// ```
/// ///
@ -51,24 +54,23 @@ declare_clippy_lint! {
} }
pub struct DisallowedTypes { pub struct DisallowedTypes {
def_ids: DefIdMap<(&'static str, Option<&'static str>)>, def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>,
prim_tys: FxHashMap<PrimTy, (&'static str, Option<&'static str>)>, prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath)>,
} }
impl DisallowedTypes { impl DisallowedTypes {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
let mut def_ids = DefIdMap::default(); let mut def_ids = DefIdMap::default();
let mut prim_tys = FxHashMap::default(); let mut prim_tys = FxHashMap::default();
for x in &conf.disallowed_types { for disallowed_path in &conf.disallowed_types {
let path: Vec<_> = x.path().split("::").collect::<Vec<_>>(); let path: Vec<_> = disallowed_path.path().split("::").collect::<Vec<_>>();
let reason = x.reason();
for res in clippy_utils::def_path_res(tcx, &path) { for res in clippy_utils::def_path_res(tcx, &path) {
match res { match res {
Res::Def(_, id) => { Res::Def(_, id) => {
def_ids.insert(id, (x.path(), reason)); def_ids.insert(id, (disallowed_path.path(), disallowed_path));
}, },
Res::PrimTy(ty) => { Res::PrimTy(ty) => {
prim_tys.insert(ty, (x.path(), reason)); prim_tys.insert(ty, (disallowed_path.path(), disallowed_path));
}, },
_ => {}, _ => {},
} }
@ -78,7 +80,7 @@ impl DisallowedTypes {
} }
fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
let (path, reason) = match res { let (path, disallowed_path) = match res {
Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x,
Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x,
_ => return, _ => return,
@ -88,11 +90,7 @@ impl DisallowedTypes {
DISALLOWED_TYPES, DISALLOWED_TYPES,
span, span,
format!("use of a disallowed type `{path}`"), format!("use of a disallowed type `{path}`"),
|diag| { disallowed_path.diag_amendment(span),
if let Some(reason) = reason {
diag.note(reason);
}
},
); );
} }
} }

View file

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::EarlyContext; use rustc_lint::EarlyContext;
use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle};
use super::DOC_INCLUDE_WITHOUT_CFG; use super::DOC_INCLUDE_WITHOUT_CFG;

View file

@ -16,7 +16,6 @@ fn map_container_to_text(c: &super::Container) -> &'static str {
} }
} }
// TODO: Adjust the parameters as necessary
pub(super) fn check( pub(super) fn check(
cx: &LateContext<'_>, cx: &LateContext<'_>,
doc: &str, doc: &str,

View file

@ -82,6 +82,28 @@ declare_clippy_lint! {
"presence of `_`, `::` or camel-case outside backticks in documentation" "presence of `_`, `::` or camel-case outside backticks in documentation"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for links with code directly adjacent to code text:
/// `` [`MyItem`]`<`[`u32`]`>` ``.
///
/// ### Why is this bad?
/// It can be written more simply using HTML-style `<code>` tags.
///
/// ### Example
/// ```no_run
/// //! [`first`](x)`second`
/// ```
/// Use instead:
/// ```no_run
/// //! <code>[first](x)second</code>
/// ```
#[clippy::version = "1.86.0"]
pub DOC_LINK_CODE,
nursery,
"link with code back-to-back with other code"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for the doc comments of publicly visible /// Checks for the doc comments of publicly visible
@ -453,7 +475,7 @@ declare_clippy_lint! {
/// /// and this line is overindented. /// /// and this line is overindented.
/// # fn foo() {} /// # fn foo() {}
/// ``` /// ```
#[clippy::version = "1.80.0"] #[clippy::version = "1.86.0"]
pub DOC_OVERINDENTED_LIST_ITEMS, pub DOC_OVERINDENTED_LIST_ITEMS,
style, style,
"ensure list items are not overindented" "ensure list items are not overindented"
@ -513,7 +535,7 @@ declare_clippy_lint! {
/// ```no_run /// ```no_run
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))] /// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
/// ``` /// ```
#[clippy::version = "1.84.0"] #[clippy::version = "1.85.0"]
pub DOC_INCLUDE_WITHOUT_CFG, pub DOC_INCLUDE_WITHOUT_CFG,
restriction, restriction,
"check if files included in documentation are behind `cfg(doc)`" "check if files included in documentation are behind `cfg(doc)`"
@ -539,7 +561,7 @@ declare_clippy_lint! {
/// //! /// //!
/// //! [link]: destination (for link reference definition) /// //! [link]: destination (for link reference definition)
/// ``` /// ```
#[clippy::version = "1.84.0"] #[clippy::version = "1.85.0"]
pub DOC_NESTED_REFDEFS, pub DOC_NESTED_REFDEFS,
suspicious, suspicious,
"link reference defined in list item or quote" "link reference defined in list item or quote"
@ -560,6 +582,7 @@ impl Documentation {
} }
impl_lint_pass!(Documentation => [ impl_lint_pass!(Documentation => [
DOC_LINK_CODE,
DOC_LINK_WITH_QUOTES, DOC_LINK_WITH_QUOTES,
DOC_MARKDOWN, DOC_MARKDOWN,
DOC_NESTED_REFDEFS, DOC_NESTED_REFDEFS,
@ -577,7 +600,6 @@ impl_lint_pass!(Documentation => [
DOC_INCLUDE_WITHOUT_CFG, DOC_INCLUDE_WITHOUT_CFG,
]); ]);
impl EarlyLintPass for Documentation { impl EarlyLintPass for Documentation {
fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) { fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
include_in_doc_without_cfg::check(cx, attrs); include_in_doc_without_cfg::check(cx, attrs);
@ -717,7 +739,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
let (fragments, _) = attrs_to_doc_fragments( let (fragments, _) = attrs_to_doc_fragments(
attrs.iter().filter_map(|attr| { attrs.iter().filter_map(|attr| {
if !attr.doc_str_and_comment_kind().is_some() || attr.span().in_external_macro(cx.sess().source_map()) { if attr.doc_str_and_comment_kind().is_none() || attr.span().in_external_macro(cx.sess().source_map()) {
None None
} else { } else {
Some((attr, None)) Some((attr, None))
@ -747,6 +769,21 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
let mut cb = fake_broken_link_callback; let mut cb = fake_broken_link_callback;
check_for_code_clusters(
cx,
pulldown_cmark::Parser::new_with_broken_link_callback(
&doc,
main_body_opts() - Options::ENABLE_SMART_PUNCTUATION,
Some(&mut cb),
)
.into_offset_iter(),
&doc,
Fragments {
doc: &doc,
fragments: &fragments,
},
);
// disable smart punctuation to pick up ['link'] more easily // disable smart punctuation to pick up ['link'] more easily
let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION; let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;
let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb)); let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb));
@ -770,6 +807,66 @@ enum Container {
List(usize), List(usize),
} }
/// Scan the documentation for code links that are back-to-back with code spans.
///
/// This is done separately from the rest of the docs, because that makes it easier to produce
/// the correct messages.
fn check_for_code_clusters<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
cx: &LateContext<'_>,
events: Events,
doc: &str,
fragments: Fragments<'_>,
) {
let mut events = events.peekable();
let mut code_starts_at = None;
let mut code_ends_at = None;
let mut code_includes_link = false;
while let Some((event, range)) = events.next() {
match event {
Start(Link { .. }) if matches!(events.peek(), Some((Code(_), _range))) => {
if code_starts_at.is_some() {
code_ends_at = Some(range.end);
} else {
code_starts_at = Some(range.start);
}
code_includes_link = true;
// skip the nested "code", because we're already handling it here
let _ = events.next();
},
Code(_) => {
if code_starts_at.is_some() {
code_ends_at = Some(range.end);
} else {
code_starts_at = Some(range.start);
}
},
End(TagEnd::Link) => {},
_ => {
if let Some(start) = code_starts_at
&& let Some(end) = code_ends_at
&& code_includes_link
{
if let Some(span) = fragments.span(cx, start..end) {
span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
diag.span_suggestion_verbose(
span,
"wrap the entire group in `<code>` tags",
sugg,
Applicability::MaybeIncorrect,
);
diag.help("separate code snippets will be shown with a gap");
});
}
}
code_includes_link = false;
code_starts_at = None;
code_ends_at = None;
},
}
}
}
/// Checks parsed documentation. /// Checks parsed documentation.
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
/// so lints here will generally access that information. /// so lints here will generally access that information.
@ -912,7 +1009,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
// backslashes aren't in the event stream... // backslashes aren't in the event stream...
start -= 1; start -= 1;
} }
start - range.start
if start > range.start {
start - range.start
} else {
0
}
} }
} else { } else {
0 0
@ -1092,6 +1194,10 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
#[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type #[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type
fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> { fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {
if range.end < range.start {
return None;
}
let offset = range.start; let offset = range.start;
let mut iterator = doc.as_bytes()[range].iter().copied().enumerate(); let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();
let mut start = None; let mut start = None;

View file

@ -1,6 +1,6 @@
use std::ops::Range; use std::ops::Range;
use std::{io, thread};
use std::sync::Arc; use std::sync::Arc;
use std::{io, thread};
use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST}; use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::AttrStyle; use rustc_ast::AttrStyle;
use rustc_ast::token::CommentKind; use rustc_ast::token::CommentKind;
use rustc_attr_parsing::AttributeKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_attr_parsing::AttributeKind;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
@ -37,7 +37,12 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
attrs attrs
.iter() .iter()
.filter_map(|attr| { .filter_map(|attr| {
if let Attribute::Parsed(AttributeKind::DocComment{ style: AttrStyle::Outer, kind, comment, ..}) = attr if let Attribute::Parsed(AttributeKind::DocComment {
style: AttrStyle::Outer,
kind,
comment,
..
}) = attr
&& let Some(com) = comment.as_str().strip_prefix('!') && let Some(com) = comment.as_str().strip_prefix('!')
{ {
let sugg = match kind { let sugg = match kind {

View file

@ -1,6 +1,6 @@
use rustc_attr_parsing::AttributeKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Attribute, Item, ItemKind}; use rustc_hir::{Attribute, Item, ItemKind};
use rustc_attr_parsing::AttributeKind;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
@ -44,7 +44,7 @@ pub(super) fn check(
let mut should_suggest_empty_doc = false; let mut should_suggest_empty_doc = false;
for attr in attrs { for attr in attrs {
if let Attribute::Parsed(AttributeKind::DocComment {span, comment, ..}) = attr { if let Attribute::Parsed(AttributeKind::DocComment { span, comment, .. }) = attr {
spans.push(span); spans.push(span);
let doc = comment.as_str(); let doc = comment.as_str();
let doc = doc.trim(); let doc = doc.trim();
@ -52,7 +52,11 @@ pub(super) fn check(
// We make this suggestion only if the first doc line ends with a punctuation // We make this suggestion only if the first doc line ends with a punctuation
// because it might just need to add an empty line with `///`. // because it might just need to add an empty line with `///`.
should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?'); should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?');
} else if spans.len() == 2 {
// We make this suggestion only if the second doc line is not empty.
should_suggest_empty_doc &= !doc.is_empty();
} }
let len = doc.chars().count(); let len = doc.chars().count();
if len >= first_paragraph_len { if len >= first_paragraph_len {
break; break;

View file

@ -289,10 +289,13 @@ impl EmptyLineAfter {
format!("empty {lines} after {kind_desc}"), format!("empty {lines} after {kind_desc}"),
|diag| { |diag| {
let info = self.items.last().unwrap(); let info = self.items.last().unwrap();
diag.span_label(info.span, match kind { diag.span_label(
StopKind::Attr => format!("the attribute applies to this {}", info.kind), info.span,
StopKind::Doc(_) => format!("the comment documents this {}", info.kind), match kind {
}); StopKind::Attr => format!("the attribute applies to this {}", info.kind),
StopKind::Doc(_) => format!("the comment documents this {}", info.kind),
},
);
diag.multipart_suggestion_with_style( diag.multipart_suggestion_with_style(
format!("if the empty {lines} {are} unintentional, remove {them}"), format!("if the empty {lines} {are} unintentional, remove {them}"),

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{ use clippy_utils::{
SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
peel_hir_expr_while, peel_hir_expr_while,
@ -7,11 +8,12 @@ use clippy_utils::{
use core::fmt::{self, Write}; use core::fmt::{self, Write};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::hir_id::HirIdSet; use rustc_hir::hir_id::HirIdSet;
use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp}; use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym}; use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym};
use std::ops::ControlFlow;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -135,8 +137,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
format!( format!(
"match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
{indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), reindent_multiline(&then_str, true, Some(4 + indent_str.len())),
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), reindent_multiline(&else_str, true, Some(4 + indent_str.len())),
entry = map_ty.entry_path(), entry = map_ty.entry_path(),
) )
} }
@ -329,7 +331,7 @@ impl<'tcx> Edit<'tcx> {
if let Self::Insertion(i) = self { Some(i) } else { None } if let Self::Insertion(i) = self { Some(i) } else { None }
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
struct Insertion<'tcx> { struct Insertion<'tcx> {
call: &'tcx Expr<'tcx>, call: &'tcx Expr<'tcx>,
value: &'tcx Expr<'tcx>, value: &'tcx Expr<'tcx>,
@ -500,7 +502,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
self.visit_non_tail_expr(insert_expr.value); self.visit_non_tail_expr(insert_expr.value);
self.is_single_insert = is_single_insert; self.is_single_insert = is_single_insert;
}, },
_ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => { _ if is_any_expr_in_map_used(self.cx, self.map, expr) => {
self.is_map_used = true; self.is_map_used = true;
}, },
_ => match expr.kind { _ => match expr.kind {
@ -542,6 +544,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
ExprKind::InlineAsm(_) => { ExprKind::InlineAsm(_) => {
self.can_use_entry = false; self.can_use_entry = false;
}, },
ExprKind::Closure(closure) => walk_body(self, self.cx.tcx.hir_body(closure.body)),
_ => { _ => {
self.allow_insert_closure &= !self.in_tail_pos; self.allow_insert_closure &= !self.in_tail_pos;
self.allow_insert_closure &= self.allow_insert_closure &=
@ -562,6 +565,19 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
} }
} }
/// Check if the given expression is used for each sub-expression in the given map.
/// For example, in map `a.b.c.my_map`, The expression `a.b.c.my_map`, `a.b.c`, `a.b`, and `a` are
/// all checked.
fn is_any_expr_in_map_used<'tcx>(cx: &LateContext<'tcx>, map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
for_each_expr(cx, map, |e| {
if SpanlessEq::new(cx).eq_expr(e, expr) {
return ControlFlow::Break(());
}
ControlFlow::Continue(())
})
.is_some()
}
struct InsertSearchResults<'tcx> { struct InsertSearchResults<'tcx> {
edits: Vec<Edit<'tcx>>, edits: Vec<Edit<'tcx>>,
allow_insert_closure: bool, allow_insert_closure: bool,

View file

@ -1,5 +1,6 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_hir; use clippy_utils::diagnostics::span_lint_hir;
use rustc_abi::ExternAbi;
use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit}; use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -10,7 +11,6 @@ use rustc_session::impl_lint_pass;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_abi::ExternAbi;
pub struct BoxedLocal { pub struct BoxedLocal {
too_large_for_stack: u64, too_large_for_stack: u64,

View file

@ -6,6 +6,7 @@ use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{ use clippy_utils::{
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
}; };
use rustc_abi::ExternAbi;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
@ -15,7 +16,6 @@ use rustc_middle::ty::{
}; };
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_abi::ExternAbi;
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
declare_clippy_lint! { declare_clippy_lint! {

View file

@ -1,13 +1,13 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
use rustc_abi::ExternAbi;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_abi::ExternAbi;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does

View file

@ -1,26 +1,28 @@
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::{ use clippy_utils::macros::{
FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span, FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call, format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
root_macro_call_first_node, root_macro_call_first_node,
}; };
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt; use clippy_utils::source::{SpanRangeExt, snippet};
use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::ty::{implements_trait, is_type_lang_item};
use clippy_utils::{is_diag_trait_item, is_from_proc_macro};
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::{ use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
FormatPlaceholder, FormatTrait, FormatPlaceholder, FormatTrait,
}; };
use rustc_attr_parsing::RustcVersion;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::Ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::{List, Ty, TyCtxt};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::edition::Edition::Edition2021; use rustc_span::edition::Edition::Edition2021;
use rustc_span::{Span, Symbol, sym}; use rustc_span::{Span, Symbol, sym};
@ -50,6 +52,36 @@ declare_clippy_lint! {
"`format!` used in a macro that does formatting" "`format!` used in a macro that does formatting"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`.
///
/// ### Why is this bad?
/// Rust doesn't guarantee what `Debug` formatting looks like, and it could
/// change in the future. `OsStr`s and `Path`s can be `Display` formatted
/// using their `display` methods.
///
/// Furthermore, with `Debug` formatting, certain characters are escaped.
/// Thus, a `Debug` formatted `Path` is less likely to be clickable.
///
/// ### Example
/// ```no_run
/// # use std::path::Path;
/// let path = Path::new("...");
/// println!("The path is {:?}", path);
/// ```
/// Use instead:
/// ```no_run
/// # use std::path::Path;
/// let path = Path::new("…");
/// println!("The path is {}", path.display());
/// ```
#[clippy::version = "1.87.0"]
pub UNNECESSARY_DEBUG_FORMATTING,
pedantic,
"`Debug` formatting applied to an `OsStr` or `Path` when `.display()` is available"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string) /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
@ -162,31 +194,35 @@ declare_clippy_lint! {
"use of a format specifier that has no effect" "use of a format specifier that has no effect"
} }
impl_lint_pass!(FormatArgs => [ impl_lint_pass!(FormatArgs<'_> => [
FORMAT_IN_FORMAT_ARGS, FORMAT_IN_FORMAT_ARGS,
TO_STRING_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS,
UNINLINED_FORMAT_ARGS, UNINLINED_FORMAT_ARGS,
UNNECESSARY_DEBUG_FORMATTING,
UNUSED_FORMAT_SPECS, UNUSED_FORMAT_SPECS,
]); ]);
#[allow(clippy::struct_field_names)] #[allow(clippy::struct_field_names)]
pub struct FormatArgs { pub struct FormatArgs<'tcx> {
format_args: FormatArgsStorage, format_args: FormatArgsStorage,
msrv: Msrv, msrv: Msrv,
ignore_mixed: bool, ignore_mixed: bool,
ty_msrv_map: FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
} }
impl FormatArgs { impl<'tcx> FormatArgs<'tcx> {
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
let ty_msrv_map = make_ty_msrv_map(tcx);
Self { Self {
format_args, format_args,
msrv: conf.msrv.clone(), msrv: conf.msrv,
ignore_mixed: conf.allow_mixed_uninlined_format_args, ignore_mixed: conf.allow_mixed_uninlined_format_args,
ty_msrv_map,
} }
} }
} }
impl<'tcx> LateLintPass<'tcx> for FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(macro_call) = root_macro_call_first_node(cx, expr) if let Some(macro_call) = root_macro_call_first_node(cx, expr)
&& is_format_macro(cx, macro_call.def_id) && is_format_macro(cx, macro_call.def_id)
@ -198,17 +234,17 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
macro_call: &macro_call, macro_call: &macro_call,
format_args, format_args,
ignore_mixed: self.ignore_mixed, ignore_mixed: self.ignore_mixed,
msrv: &self.msrv,
ty_msrv_map: &self.ty_msrv_map,
}; };
linter.check_templates(); linter.check_templates();
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { if self.msrv.meets(cx, msrvs::FORMAT_ARGS_CAPTURE) {
linter.check_uninlined_args(); linter.check_uninlined_args();
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
struct FormatArgsExpr<'a, 'tcx> { struct FormatArgsExpr<'a, 'tcx> {
@ -217,9 +253,11 @@ struct FormatArgsExpr<'a, 'tcx> {
macro_call: &'a MacroCall, macro_call: &'a MacroCall,
format_args: &'a rustc_ast::FormatArgs, format_args: &'a rustc_ast::FormatArgs,
ignore_mixed: bool, ignore_mixed: bool,
msrv: &'a Msrv,
ty_msrv_map: &'a FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
} }
impl FormatArgsExpr<'_, '_> { impl<'tcx> FormatArgsExpr<'_, 'tcx> {
fn check_templates(&self) { fn check_templates(&self) {
for piece in &self.format_args.template { for piece in &self.format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece if let FormatArgsPiece::Placeholder(placeholder) = piece
@ -237,6 +275,11 @@ impl FormatArgsExpr<'_, '_> {
self.check_format_in_format_args(name, arg_expr); self.check_format_in_format_args(name, arg_expr);
self.check_to_string_in_format_args(name, arg_expr); self.check_to_string_in_format_args(name, arg_expr);
} }
if placeholder.format_trait == FormatTrait::Debug {
let name = self.cx.tcx.item_name(self.macro_call.def_id);
self.check_unnecessary_debug_formatting(name, arg_expr);
}
} }
} }
} }
@ -439,6 +482,33 @@ impl FormatArgsExpr<'_, '_> {
} }
} }
fn check_unnecessary_debug_formatting(&self, name: Symbol, value: &Expr<'tcx>) {
let cx = self.cx;
if !value.span.from_expansion()
&& !is_from_proc_macro(cx, value)
&& let ty = cx.typeck_results().expr_ty(value)
&& self.can_display_format(ty)
{
let snippet = snippet(cx.sess(), value.span, "..");
span_lint_and_then(
cx,
UNNECESSARY_DEBUG_FORMATTING,
value.span,
format!("unnecessary `Debug` formatting in `{name}!` args"),
|diag| {
diag.help(format!(
"use `Display` formatting and change this to `{snippet}.display()`"
));
diag.note(
"switching to `Display` formatting will change how the value is shown; \
escaped characters will no longer be escaped and surrounding quotes will \
be removed",
);
},
);
}
}
fn format_arg_positions(&self) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> { fn format_arg_positions(&self) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
self.format_args.template.iter().flat_map(|piece| match piece { self.format_args.template.iter().flat_map(|piece| match piece {
FormatArgsPiece::Placeholder(placeholder) => { FormatArgsPiece::Placeholder(placeholder) => {
@ -465,6 +535,41 @@ impl FormatArgsExpr<'_, '_> {
.at_most_one() .at_most_one()
.is_err() .is_err()
} }
fn can_display_format(&self, ty: Ty<'tcx>) -> bool {
let ty = ty.peel_refs();
if let Some(msrv) = self.ty_msrv_map.get(&ty)
&& msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv))
{
return true;
}
// Even if `ty` is not in `self.ty_msrv_map`, check whether `ty` implements `Deref` with
// a `Target` that is in `self.ty_msrv_map`.
if let Some(deref_trait_id) = self.cx.tcx.lang_items().deref_trait()
&& implements_trait(self.cx, ty, deref_trait_id, &[])
&& let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, "Target")
&& let Some(msrv) = self.ty_msrv_map.get(&target_ty)
&& msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv))
{
return true;
}
false
}
}
fn make_ty_msrv_map(tcx: TyCtxt<'_>) -> FxHashMap<Ty<'_>, Option<RustcVersion>> {
[(sym::OsStr, Some(msrvs::OS_STR_DISPLAY)), (sym::Path, None)]
.into_iter()
.filter_map(|(name, feature)| {
tcx.get_diagnostic_item(name).map(|def_id| {
let ty = Ty::new_adt(tcx, tcx.adt_def(def_id), List::empty());
(ty, feature)
})
})
.collect()
} }
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>) fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)

View file

@ -58,9 +58,7 @@ pub struct FromOverInto {
impl FromOverInto { impl FromOverInto {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
FromOverInto { FromOverInto { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -77,12 +75,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
&& let Some(into_trait_seg) = hir_trait_ref.path.segments.last() && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
// `impl Into<target_ty> for self_ty` // `impl Into<target_ty> for self_ty`
&& let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
&& self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE)
&& span_is_local(item.span) && span_is_local(item.span)
&& let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
.map(ty::EarlyBinder::instantiate_identity) .map(ty::EarlyBinder::instantiate_identity)
&& cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
&& !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _)) && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _))
&& self.msrv.meets(cx, msrvs::RE_REBALANCING_COHERENCE)
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,
@ -114,8 +112,6 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
/// Finds the occurrences of `Self` and `self` /// Finds the occurrences of `Self` and `self`

View file

@ -471,7 +471,7 @@ impl Functions {
.iter() .iter()
.flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>())) .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>()))
.collect(), .collect(),
msrv: conf.msrv.clone(), msrv: conf.msrv,
} }
} }
} }
@ -521,12 +521,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
must_use::check_item(cx, item); must_use::check_item(cx, item);
result::check_item(cx, item, self.large_error_threshold, &self.msrv); result::check_item(cx, item, self.large_error_threshold, self.msrv);
} }
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
must_use::check_impl_item(cx, item); must_use::check_impl_item(cx, item);
result::check_impl_item(cx, item, self.large_error_threshold, &self.msrv); result::check_impl_item(cx, item, self.large_error_threshold, self.msrv);
impl_trait_in_params::check_impl_item(cx, item); impl_trait_in_params::check_impl_item(cx, item);
renamed_function_params::check_impl_item(cx, item, &self.trait_ids); renamed_function_params::check_impl_item(cx, item, &self.trait_ids);
} }
@ -535,10 +535,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
not_unsafe_ptr_arg_deref::check_trait_item(cx, item); not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
must_use::check_trait_item(cx, item); must_use::check_trait_item(cx, item);
result::check_trait_item(cx, item, self.large_error_threshold, &self.msrv); result::check_trait_item(cx, item, self.large_error_threshold, self.msrv);
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api);
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -118,18 +118,23 @@ fn check_needless_must_use(
fn_header_span, fn_header_span,
"this unit-returning function has a `#[must_use]` attribute", "this unit-returning function has a `#[must_use]` attribute",
|diag| { |diag| {
diag.span_suggestion(attr.span(), "remove the attribute", "", Applicability::MachineApplicable); diag.span_suggestion(
attr.span(),
"remove the attribute",
"",
Applicability::MachineApplicable,
);
}, },
); );
} else { } else {
// When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
// issue #12320. // issue #12320.
// FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile,
// honestly looked incorrect, and is a little hard to support for a little bit now. Some day this could be // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this
// re-added. // could be re-added.
span_lint_and_help( span_lint_and_help(
cx, cx,
DOUBLE_MUST_USE, MUST_USE_UNIT,
fn_header_span, fn_header_span,
"this unit-returning function has a `#[must_use]` attribute", "this unit-returning function has a `#[must_use]` attribute",
Some(attr.span()), Some(attr.span()),

View file

@ -34,7 +34,7 @@ fn result_err_ty<'tcx>(
} }
} }
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64, msrv: &Msrv) { pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64, msrv: Msrv) {
if let hir::ItemKind::Fn { ref sig, .. } = item.kind if let hir::ItemKind::Fn { ref sig, .. } = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span)
{ {
@ -50,7 +50,7 @@ pub(super) fn check_impl_item<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
item: &hir::ImplItem<'tcx>, item: &hir::ImplItem<'tcx>,
large_err_threshold: u64, large_err_threshold: u64,
msrv: &Msrv, msrv: Msrv,
) { ) {
// Don't lint if method is a trait's implementation, we can't do anything about those // Don't lint if method is a trait's implementation, we can't do anything about those
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
@ -69,7 +69,7 @@ pub(super) fn check_trait_item<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
item: &hir::TraitItem<'tcx>, item: &hir::TraitItem<'tcx>,
large_err_threshold: u64, large_err_threshold: u64,
msrv: &Msrv, msrv: Msrv,
) { ) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@ -82,8 +82,8 @@ pub(super) fn check_trait_item<'tcx>(
} }
} }
fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span, msrv: &Msrv) { fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span, msrv: Msrv) {
if err_ty.is_unit() && (!is_no_std_crate(cx) || msrv.meets(msrvs::ERROR_IN_CORE)) { if err_ty.is_unit() && (!is_no_std_crate(cx) || msrv.meets(cx, msrvs::ERROR_IN_CORE)) {
span_lint_and_help( span_lint_and_help(
cx, cx,
RESULT_UNIT_ERR, RESULT_UNIT_ERR,

View file

@ -1,7 +1,7 @@
use rustc_abi::ExternAbi;
use rustc_hir::{self as hir, intravisit}; use rustc_hir::{self as hir, intravisit};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
use rustc_abi::ExternAbi;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_trait_impl_item; use clippy_utils::is_trait_impl_item;
@ -23,11 +23,19 @@ pub(super) fn check_fn(
intravisit::FnKind::Method( intravisit::FnKind::Method(
_, _,
&hir::FnSig { &hir::FnSig {
header: hir::FnHeader { abi: ExternAbi::Rust, .. }, header: hir::FnHeader {
abi: ExternAbi::Rust, ..
},
.. ..
}, },
) )
| intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: ExternAbi::Rust, .. }) => check_arg_number( | intravisit::FnKind::ItemFn(
_,
_,
hir::FnHeader {
abi: ExternAbi::Rust, ..
},
) => check_arg_number(
cx, cx,
decl, decl,
span.with_hi(decl.output.span().hi()), span.with_hi(decl.output.span().hi()),

View file

@ -7,7 +7,6 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::Span; use rustc_span::Span;
use std::borrow::Cow;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -107,7 +106,7 @@ fn make_sugg<'a>(
els_span: Span, els_span: Span,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
) -> Cow<'a, str> { ) -> String {
let cond_inner_snip = snippet(sess, cond_inner, default); let cond_inner_snip = snippet(sess, cond_inner, default);
let els_snip = snippet(sess, els_span, default); let els_snip = snippet(sess, els_span, default);
let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
@ -130,5 +129,5 @@ fn make_sugg<'a>(
_ => String::new(), _ => String::new(),
}; };
reindent_multiline(suggestion.into(), true, indent) reindent_multiline(&suggestion, true, indent)
} }

View file

@ -54,9 +54,7 @@ pub struct IfThenSomeElseNone {
impl IfThenSomeElseNone { impl IfThenSomeElseNone {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -79,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
&& !is_else_clause(cx.tcx, expr) && !is_else_clause(cx.tcx, expr)
&& !is_in_const_context(cx) && !is_in_const_context(cx)
&& !expr.span.in_external_macro(cx.sess().source_map()) && !expr.span.in_external_macro(cx.sess().source_map())
&& self.msrv.meets(msrvs::BOOL_THEN) && self.msrv.meets(cx, msrvs::BOOL_THEN)
&& !contains_return(then_block.stmts) && !contains_return(then_block.stmts)
{ {
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
"then_some" "then_some"
} else { } else {
"then" "then"
@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
expr.span, expr.span,
format!("this could be simplified with `bool::{method_name}`"), format!("this could be simplified with `bool::{method_name}`"),
|diag| { |diag| {
let mut app = Applicability::Unspecified; let mut app = Applicability::MachineApplicable;
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
.maybe_par() .maybe_par()
.to_string(); .to_string();
@ -120,6 +118,4 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -83,9 +83,7 @@ impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATU
impl ImplicitSaturatingSub { impl ImplicitSaturatingSub {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -108,12 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
&& let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
{ {
check_manual_check( check_manual_check(
cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv, cx, expr, cond_op, cond_left, cond_right, if_block, else_block, self.msrv,
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -125,7 +121,7 @@ fn check_manual_check<'tcx>(
right_hand: &Expr<'tcx>, right_hand: &Expr<'tcx>,
if_block: &Expr<'tcx>, if_block: &Expr<'tcx>,
else_block: &Expr<'tcx>, else_block: &Expr<'tcx>,
msrv: &Msrv, msrv: Msrv,
) { ) {
let ty = cx.typeck_results().expr_ty(left_hand); let ty = cx.typeck_results().expr_ty(left_hand);
if ty.is_numeric() && !ty.is_signed() { if ty.is_numeric() && !ty.is_signed() {
@ -178,7 +174,7 @@ fn check_gt(
little_var: &Expr<'_>, little_var: &Expr<'_>,
if_block: &Expr<'_>, if_block: &Expr<'_>,
else_block: &Expr<'_>, else_block: &Expr<'_>,
msrv: &Msrv, msrv: Msrv,
is_composited: bool, is_composited: bool,
) { ) {
if let Some(big_var) = Var::new(big_var) if let Some(big_var) = Var::new(big_var)
@ -221,7 +217,7 @@ fn check_subtraction(
little_var: Var, little_var: Var,
if_block: &Expr<'_>, if_block: &Expr<'_>,
else_block: &Expr<'_>, else_block: &Expr<'_>,
msrv: &Msrv, msrv: Msrv,
is_composited: bool, is_composited: bool,
) { ) {
let if_block = peel_blocks(if_block); let if_block = peel_blocks(if_block);
@ -258,7 +254,7 @@ fn check_subtraction(
// if `snippet_opt` fails, it won't try the next conditions. // if `snippet_opt` fails, it won't try the next conditions.
if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) if let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
&& let Some(little_var_snippet) = snippet_opt(cx, little_var.span) && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
&& (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST))
{ {
let sugg = format!( let sugg = format!(
"{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}",

View file

@ -42,6 +42,7 @@ declare_clippy_lint! {
pub struct IncompatibleMsrv { pub struct IncompatibleMsrv {
msrv: Msrv, msrv: Msrv,
is_above_msrv: FxHashMap<DefId, RustcVersion>, is_above_msrv: FxHashMap<DefId, RustcVersion>,
check_in_tests: bool,
} }
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
@ -49,8 +50,9 @@ impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
impl IncompatibleMsrv { impl IncompatibleMsrv {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: conf.msrv,
is_above_msrv: FxHashMap::default(), is_above_msrv: FxHashMap::default(),
check_in_tests: conf.check_incompatible_msrv_in_tests,
} }
} }
@ -86,39 +88,30 @@ impl IncompatibleMsrv {
// We don't check local items since their MSRV is supposed to always be valid. // We don't check local items since their MSRV is supposed to always be valid.
return; return;
} }
let version = self.get_def_id_version(cx.tcx, def_id);
if self.msrv.meets(version) || is_in_test(cx.tcx, node) {
return;
}
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind { if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind {
// Desugared expressions get to cheat and stability is ignored. // Desugared expressions get to cheat and stability is ignored.
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions // Intentionally not using `.from_expansion()`, since we do still care about macro expansions
return; return;
} }
self.emit_lint_for(cx, span, version); if (self.check_in_tests || !is_in_test(cx.tcx, node))
} && let Some(current) = self.msrv.current(cx)
&& let version = self.get_def_id_version(cx.tcx, def_id)
fn emit_lint_for(&self, cx: &LateContext<'_>, span: Span, version: RustcVersion) { && version > current
span_lint( {
cx, span_lint(
INCOMPATIBLE_MSRV, cx,
span, INCOMPATIBLE_MSRV,
format!( span,
"current MSRV (Minimum Supported Rust Version) is `{}` but this item is stable since `{version}`", format!(
self.msrv "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable since `{version}`"
), ),
); );
}
} }
} }
impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
extract_msrv_attr!(LateContext);
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if self.msrv.current().is_none() {
// If there is no MSRV, then no need to check anything...
return;
}
match expr.kind { match expr.kind {
ExprKind::MethodCall(_, _, _, span) => { ExprKind::MethodCall(_, _, _, span) => {
if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {

View file

@ -62,7 +62,7 @@ impl IndexRefutableSlice {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
max_suggested_slice: conf.max_suggested_slice_pattern_length, max_suggested_slice: conf.max_suggested_slice_pattern_length,
msrv: conf.msrv.clone(), msrv: conf.msrv,
} }
} }
} }
@ -74,19 +74,17 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr)
&& (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some())
&& !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id)
&& self.msrv.meets(msrvs::SLICE_PATTERNS)
&& let found_slices = find_slice_values(cx, let_pat) && let found_slices = find_slice_values(cx, let_pat)
&& !found_slices.is_empty() && !found_slices.is_empty()
&& let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then) && let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then)
&& !filtered_slices.is_empty() && !filtered_slices.is_empty()
&& self.msrv.meets(cx, msrvs::SLICE_PATTERNS)
{ {
for slice in filtered_slices.values() { for slice in filtered_slices.values() {
lint_slice(cx, slice); lint_slice(cx, slice);
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<HirId, SliceLintInformation> { fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<HirId, SliceLintInformation> {

View file

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::ty::{implements_trait, is_type_lang_item};
use clippy_utils::{return_ty, trait_ref_of_method}; use clippy_utils::{return_ty, trait_ref_of_method};
use rustc_abi::ExternAbi;
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::sym; use rustc_span::sym;
use rustc_abi::ExternAbi;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does

View file

@ -70,9 +70,7 @@ pub struct InstantSubtraction {
impl InstantSubtraction { impl InstantSubtraction {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -99,14 +97,12 @@ impl LateLintPass<'_> for InstantSubtraction {
print_manual_instant_elapsed_sugg(cx, expr, sugg); print_manual_instant_elapsed_sugg(cx, expr, sugg);
} else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration)
&& !expr.span.from_expansion() && !expr.span.from_expansion()
&& self.msrv.meets(msrvs::TRY_FROM) && self.msrv.meets(cx, msrvs::TRY_FROM)
{ {
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr);
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {

View file

@ -22,9 +22,6 @@ declare_clippy_lint! {
/// will mistakenly imply that it is possible for `x` to be outside the range of /// will mistakenly imply that it is possible for `x` to be outside the range of
/// `u8`. /// `u8`.
/// ///
/// ### Known problems
/// https://github.com/rust-lang/rust-clippy/issues/886
///
/// ### Example /// ### Example
/// ```no_run /// ```no_run
/// let x: u8 = 1; /// let x: u8 = 1;

View file

@ -2,9 +2,8 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use rustc_ast::LitKind; use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_ast::{Attribute, AttrArgs, AttrKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::sym; use rustc_span::sym;

View file

@ -39,9 +39,7 @@ pub struct LegacyNumericConstants {
impl LegacyNumericConstants { impl LegacyNumericConstants {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -52,9 +50,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
// Integer modules are "TBD" deprecated, and the contents are too, // Integer modules are "TBD" deprecated, and the contents are too,
// so lint on the `use` statement directly. // so lint on the `use` statement directly.
if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind
&& self.msrv.meets(msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
&& !item.span.in_external_macro(cx.sess().source_map()) && !item.span.in_external_macro(cx.sess().source_map())
&& let Some(def_id) = path.res[0].opt_def_id() && let Some(def_id) = path.res[0].opt_def_id()
&& self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
{ {
let module = if is_integer_module(cx, def_id) { let module = if is_integer_module(cx, def_id) {
true true
@ -137,8 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
return; return;
}; };
if self.msrv.meets(msrvs::NUMERIC_ASSOCIATED_CONSTANTS) if !expr.span.in_external_macro(cx.sess().source_map())
&& !expr.span.in_external_macro(cx.sess().source_map()) && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
&& !is_from_proc_macro(cx, expr) && !is_from_proc_macro(cx, expr)
{ {
span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
@ -151,8 +149,6 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
}); });
} }
} }
extract_msrv_attr!(LateContext);
} }
fn is_integer_module(cx: &LateContext<'_>, did: DefId) -> bool { fn is_integer_module(cx: &LateContext<'_>, did: DefId) -> bool {

View file

@ -340,6 +340,7 @@ mod significant_drop_tightening;
mod single_call_fn; mod single_call_fn;
mod single_char_lifetime_names; mod single_char_lifetime_names;
mod single_component_path_imports; mod single_component_path_imports;
mod single_option_map;
mod single_range_in_vec_init; mod single_range_in_vec_init;
mod size_of_in_element_count; mod size_of_in_element_count;
mod size_of_ref; mod size_of_ref;
@ -841,7 +842,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))); store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf)));
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf)));
let format_args = format_args_storage.clone(); let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(conf, format_args.clone()))); store.register_late_pass(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone())));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
@ -904,7 +905,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));
store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk)); store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf)));
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf)));
@ -948,7 +949,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default()); store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default());
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity)); store.register_late_pass(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf)));
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()); store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default());
@ -982,5 +983,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default()); store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -38,8 +38,8 @@ declare_clippy_lint! {
/// them leads to more readable code. /// them leads to more readable code.
/// ///
/// ### Known problems /// ### Known problems
/// - We bail out if the function has a `where` clause where lifetimes /// This lint ignores functions with `where` clauses that reference
/// are mentioned due to potential false positives. /// lifetimes to prevent false positives.
/// ///
/// ### Example /// ### Example
/// ```no_run /// ```no_run
@ -62,6 +62,38 @@ declare_clippy_lint! {
would allow omitting them" would allow omitting them"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
///
/// ### Why is this bad?
/// The additional lifetimes can make the code look more complicated.
///
/// ### Known problems
/// This lint ignores functions with `where` clauses that reference
/// lifetimes to prevent false positives.
///
/// ### Example
/// ```no_run
/// # use std::str::Chars;
/// fn f<'a>(x: &'a str) -> Chars<'a> {
/// x.chars()
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::str::Chars;
/// fn f(x: &str) -> Chars<'_> {
/// x.chars()
/// }
/// ```
#[clippy::version = "1.84.0"]
pub ELIDABLE_LIFETIME_NAMES,
pedantic,
"lifetime name that can be replaced with the anonymous lifetime"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for lifetimes in generics that are never used /// Checks for lifetimes in generics that are never used
@ -98,13 +130,15 @@ pub struct Lifetimes {
impl Lifetimes { impl Lifetimes {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl_lint_pass!(Lifetimes => [
NEEDLESS_LIFETIMES,
ELIDABLE_LIFETIME_NAMES,
EXTRA_UNUSED_LIFETIMES,
]);
impl<'tcx> LateLintPass<'tcx> for Lifetimes { impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@ -115,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
.. ..
} = item.kind } = item.kind
{ {
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv); check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
} else if let ItemKind::Impl(impl_) = item.kind { } else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() { if !item.span.from_expansion() {
report_extra_impl_lifetimes(cx, impl_); report_extra_impl_lifetimes(cx, impl_);
@ -134,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
item.generics, item.generics,
item.span, item.span,
report_extra_lifetimes, report_extra_lifetimes,
&self.msrv, self.msrv,
); );
} }
} }
@ -145,11 +179,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None), TraitFn::Provided(id) => (Some(id), None),
}; };
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv); check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, self.msrv);
} }
} }
extract_msrv_attr!(LateContext);
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -161,7 +193,7 @@ fn check_fn_inner<'tcx>(
generics: &'tcx Generics<'_>, generics: &'tcx Generics<'_>,
span: Span, span: Span,
report_extra_lifetimes: bool, report_extra_lifetimes: bool,
msrv: &Msrv, msrv: Msrv,
) { ) {
if span.in_external_macro(cx.sess().source_map()) || has_where_lifetimes(cx, generics) { if span.in_external_macro(cx.sess().source_map()) || has_where_lifetimes(cx, generics) {
return; return;
@ -234,7 +266,7 @@ fn could_use_elision<'tcx>(
body: Option<BodyId>, body: Option<BodyId>,
trait_sig: Option<&[Ident]>, trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>], named_generics: &'tcx [GenericParam<'_>],
msrv: &Msrv, msrv: Msrv,
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> { ) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
// There are two scenarios where elision works: // There are two scenarios where elision works:
// * no output references, all input references have different LT // * no output references, all input references have different LT
@ -352,17 +384,12 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
} }
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064 // elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
fn non_elidable_self_type<'tcx>( fn non_elidable_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>, msrv: Msrv) -> bool {
cx: &LateContext<'tcx>, if let Some(ident) = ident
func: &FnDecl<'tcx>,
ident: Option<Ident>,
msrv: &Msrv,
) -> bool {
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
&& let Some(ident) = ident
&& ident.name == kw::SelfLower && ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self() && !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first() && let Some(self_ty) = func.inputs.first()
&& !msrv.meets(cx, msrvs::EXPLICIT_SELF_TYPE_ELISION)
{ {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);
visitor.visit_ty_unambig(self_ty); visitor.visit_ty_unambig(self_ty);
@ -746,6 +773,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
} }
#[derive(Copy, Clone)]
enum ElidableUsage {
/// Used in a ref (`&'a T`), can be removed
Ref(Span),
/// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
/// with `'_`
Other(Span),
}
/// Generate diagnostic messages for elidable lifetimes. /// Generate diagnostic messages for elidable lifetimes.
fn report_elidable_lifetimes( fn report_elidable_lifetimes(
cx: &LateContext<'_>, cx: &LateContext<'_>,
@ -763,9 +799,29 @@ fn report_elidable_lifetimes(
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
let elidable_usages: Vec<ElidableUsage> = usages
.iter()
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
.map(|usage| match cx.tcx.parent_hir_node(usage.hir_id) {
Node::Ty(Ty {
kind: TyKind::Ref(..), ..
}) => ElidableUsage::Ref(usage.ident.span),
_ => ElidableUsage::Other(usage.ident.span),
})
.collect();
let lint = if elidable_usages
.iter()
.any(|usage| matches!(usage, ElidableUsage::Other(_)))
{
ELIDABLE_LIFETIME_NAMES
} else {
NEEDLESS_LIFETIMES
};
span_lint_and_then( span_lint_and_then(
cx, cx,
NEEDLESS_LIFETIMES, lint,
elidable_lts elidable_lts
.iter() .iter()
.map(|&lt| cx.tcx.def_span(lt)) .map(|&lt| cx.tcx.def_span(lt))
@ -785,7 +841,7 @@ fn report_elidable_lifetimes(
return; return;
} }
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, &elidable_usages) {
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
} }
}, },
@ -796,7 +852,7 @@ fn elision_suggestions(
cx: &LateContext<'_>, cx: &LateContext<'_>,
generics: &Generics<'_>, generics: &Generics<'_>,
elidable_lts: &[LocalDefId], elidable_lts: &[LocalDefId],
usages: &[Lifetime], usages: &[ElidableUsage],
) -> Option<Vec<(Span, String)>> { ) -> Option<Vec<(Span, String)>> {
let explicit_params = generics let explicit_params = generics
.params .params
@ -836,26 +892,21 @@ fn elision_suggestions(
.collect::<Option<Vec<_>>>()? .collect::<Option<Vec<_>>>()?
}; };
suggestions.extend( suggestions.extend(usages.iter().map(|&usage| {
usages match usage {
.iter() ElidableUsage::Ref(span) => {
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id))) // expand `&'a T` to `&'a T`
.map(|usage| { // ^^ ^^^
match cx.tcx.parent_hir_node(usage.hir_id) { let span = cx.sess().source_map().span_extend_while_whitespace(span);
Node::Ty(Ty {
kind: TyKind::Ref(..), ..
}) => {
// expand `&'a T` to `&'a T`
// ^^ ^^^
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
(span, String::new()) (span, String::new())
}, },
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_` ElidableUsage::Other(span) => {
_ => (usage.ident.span, String::from("'_")), // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
} (span, String::from("'_"))
}), },
); }
}));
Some(suggestions) Some(suggestions)
} }

View file

@ -1,12 +1,24 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id}; use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_hir::{Body, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::sym; use rustc_span::sym;
pub struct LinesFilterMapOk {
msrv: Msrv,
}
impl LinesFilterMapOk {
pub fn new(conf: &Conf) -> Self {
Self { msrv: conf.msrv }
}
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` /// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
@ -55,7 +67,8 @@ declare_clippy_lint! {
suspicious, suspicious,
"filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop" "filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop"
} }
declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
impl LateLintPass<'_> for LinesFilterMapOk { impl LateLintPass<'_> for LinesFilterMapOk {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@ -65,6 +78,7 @@ impl LateLintPass<'_> for LinesFilterMapOk {
&& matches!(fm_method_str, "filter_map" | "flat_map" | "flatten") && matches!(fm_method_str, "filter_map" | "flat_map" | "flatten")
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
&& should_lint(cx, fm_args, fm_method_str) && should_lint(cx, fm_args, fm_method_str)
&& self.msrv.meets(cx, msrvs::MAP_WHILE)
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,

View file

@ -7,6 +7,7 @@ use rustc_session::declare_lint_pass;
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
use clippy_utils::mir::enclosing_mir; use clippy_utils::mir::enclosing_mir;
declare_clippy_lint! { declare_clippy_lint! {
@ -79,9 +80,9 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option<Strin
} }
} }
impl LateLintPass<'_> for LiteralStringWithFormattingArg { impl<'tcx> LateLintPass<'tcx> for LiteralStringWithFormattingArg {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if expr.span.from_expansion() { if expr.span.from_expansion() || expr.span.is_dummy() {
return; return;
} }
if let ExprKind::Lit(lit) = expr.kind { if let ExprKind::Lit(lit) = expr.kind {
@ -95,6 +96,9 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
}, },
_ => return, _ => return,
}; };
if is_from_proc_macro(cx, expr) {
return;
}
let fmt_str = symbol.as_str(); let fmt_str = symbol.as_str();
let lo = expr.span.lo(); let lo = expr.span.lo();
let mut current = fmt_str; let mut current = fmt_str;
@ -124,7 +128,11 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
pos.start += diff_len; pos.start += diff_len;
pos.end += diff_len; pos.end += diff_len;
let start = fmt_str[..pos.start].rfind('{').unwrap_or(pos.start); let mut start = pos.start;
while start < fmt_str.len() && !fmt_str.is_char_boundary(start) {
start += 1;
}
let start = fmt_str[..start].rfind('{').unwrap_or(start);
// If this is a unicode character escape, we don't want to lint. // If this is a unicode character escape, we don't want to lint.
if start > 1 && fmt_str[..start].ends_with("\\u") { if start > 1 && fmt_str[..start].ends_with("\\u") {
continue; continue;

View file

@ -17,7 +17,7 @@ pub(super) fn check(
cx: &LateContext<'_>, cx: &LateContext<'_>,
self_arg: &Expr<'_>, self_arg: &Expr<'_>,
call_expr: &Expr<'_>, call_expr: &Expr<'_>,
msrv: &Msrv, msrv: Msrv,
enforce_iter_loop_reborrow: bool, enforce_iter_loop_reborrow: bool,
) { ) {
let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else { let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else {
@ -26,10 +26,11 @@ pub(super) fn check(
if let ty::Array(_, count) = *ty.peel_refs().kind() { if let ty::Array(_, count) = *ty.peel_refs().kind() {
if !ty.is_ref() { if !ty.is_ref() {
if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { if !msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) {
return; return;
} }
} else if count.try_to_target_usize(cx.tcx).is_none_or(|x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) { } else if count.try_to_target_usize(cx.tcx).is_none_or(|x| x > 32) && !msrv.meets(cx, msrvs::ARRAY_IMPL_ANY_LEN)
{
return; return;
} }
} }
@ -106,7 +107,7 @@ fn is_ref_iterable<'tcx>(
self_arg: &Expr<'_>, self_arg: &Expr<'_>,
call_expr: &Expr<'_>, call_expr: &Expr<'_>,
enforce_iter_loop_reborrow: bool, enforce_iter_loop_reborrow: bool,
msrv: &Msrv, msrv: Msrv,
) -> Option<(AdjustKind, Ty<'tcx>)> { ) -> Option<(AdjustKind, Ty<'tcx>)> {
let typeck = cx.typeck_results(); let typeck = cx.typeck_results();
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@ -126,8 +127,8 @@ fn is_ref_iterable<'tcx>(
let self_ty = typeck.expr_ty(self_arg); let self_ty = typeck.expr_ty(self_arg);
let self_is_copy = is_copy(cx, self_ty); let self_is_copy = is_copy(cx, self_ty);
if !msrv.meets(msrvs::BOX_INTO_ITER) if is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox)
&& is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) && !msrv.meets(cx, msrvs::BOX_INTO_ITER)
{ {
return None; return None;
} }

View file

@ -1,6 +1,7 @@
use super::MANUAL_FLATTEN; use super::MANUAL_FLATTEN;
use super::utils::make_iterator_snippet; use super::utils::make_iterator_snippet;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::visitors::is_local_used; use clippy_utils::visitors::is_local_used;
use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -18,6 +19,7 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>, arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
span: Span, span: Span,
msrv: Msrv,
) { ) {
let inner_expr = peel_blocks_with_stmt(body); let inner_expr = peel_blocks_with_stmt(body);
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None, .. }) if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None, .. })
@ -34,6 +36,7 @@ pub(super) fn check<'tcx>(
&& (some_ctor || ok_ctor) && (some_ctor || ok_ctor)
// Ensure expr in `if let` is not used afterwards // Ensure expr in `if let` is not used afterwards
&& !is_local_used(cx, if_then, pat_hir_id) && !is_local_used(cx, if_then, pat_hir_id)
&& msrv.meets(cx, msrvs::ITER_FLATTEN)
{ {
let if_let_type = if some_ctor { "Some" } else { "Ok" }; let if_let_type = if some_ctor { "Some" } else { "Ok" };
// Prepare the error message // Prepare the error message

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::macros::span_is_local;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{HasSession, snippet_with_applicability}; use clippy_utils::source::{HasSession, snippet_with_applicability};
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment};
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_ast::{RangeLimits, UnOp}; use rustc_ast::{RangeLimits, UnOp};
@ -24,12 +24,8 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>, arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
msrv: &Msrv, msrv: Msrv,
) { ) {
if !msrv.meets(msrvs::SLICE_FILL) {
return;
}
// `for _ in 0..slice.len() { slice[_] = value; }` // `for _ in 0..slice.len() { slice[_] = value; }`
if let Some(higher::Range { if let Some(higher::Range {
start: Some(start), start: Some(start),
@ -43,7 +39,7 @@ pub(super) fn check<'tcx>(
&& let ExprKind::Block(..) = body.kind && let ExprKind::Block(..) = body.kind
// Check if the body is an assignment to a slice element. // Check if the body is an assignment to a slice element.
&& let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind && let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind
&& let ExprKind::Index(slice, _, _) = assignee.kind && let ExprKind::Index(slice, idx, _) = assignee.kind
// Check if `len()` is used for the range end. // Check if `len()` is used for the range end.
&& let ExprKind::MethodCall(path, recv,..) = end.kind && let ExprKind::MethodCall(path, recv,..) = end.kind
&& path.ident.name == sym::len && path.ident.name == sym::len
@ -54,10 +50,14 @@ pub(super) fn check<'tcx>(
&& !assignval.span.from_expansion() && !assignval.span.from_expansion()
// It is generally not equivalent to use the `fill` method if `assignval` can have side effects // It is generally not equivalent to use the `fill` method if `assignval` can have side effects
&& switch_to_eager_eval(cx, assignval) && switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span)
// The `fill` method requires that the slice's element type implements the `Clone` trait. // The `fill` method requires that the slice's element type implements the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait() && let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[]) && implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[])
// https://github.com/rust-lang/rust-clippy/issues/14192
&& let ExprKind::Path(Resolved(_, idx_path)) = idx.kind
&& let Res::Local(idx_hir) = idx_path.res
&& !is_local_used(cx, assignval, idx_hir)
&& msrv.meets(cx, msrvs::SLICE_FILL)
{ {
sugg(cx, body, expr, slice.span, assignval.span); sugg(cx, body, expr, slice.span, assignval.span);
} }
@ -73,10 +73,12 @@ pub(super) fn check<'tcx>(
&& local == pat.hir_id && local == pat.hir_id
&& !assignval.span.from_expansion() && !assignval.span.from_expansion()
&& switch_to_eager_eval(cx, assignval) && switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span) // `assignval` must not reference the iterator
&& !is_local_used(cx, assignval, local)
// The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait. // The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait() && let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[]) && implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[])
&& msrv.meets(cx, msrvs::SLICE_FILL)
{ {
sugg(cx, body, expr, recv_path.span, assignval.span); sugg(cx, body, expr, recv_path.span, assignval.span);
} }

View file

@ -747,7 +747,7 @@ pub struct Loops {
impl Loops { impl Loops {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: conf.msrv,
enforce_iter_loop_reborrow: conf.enforce_iter_loop_reborrow, enforce_iter_loop_reborrow: conf.enforce_iter_loop_reborrow,
} }
} }
@ -832,8 +832,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
manual_while_let_some::check(cx, condition, body, span); manual_while_let_some::check(cx, condition, body, span);
} }
} }
extract_msrv_attr!(LateContext);
} }
impl Loops { impl Loops {
@ -850,7 +848,7 @@ impl Loops {
) { ) {
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
if !is_manual_memcpy_triggered { if !is_manual_memcpy_triggered {
manual_slice_fill::check(cx, pat, arg, body, expr, &self.msrv); manual_slice_fill::check(cx, pat, arg, body, expr, self.msrv);
needless_range_loop::check(cx, pat, arg, body, expr); needless_range_loop::check(cx, pat, arg, body, expr);
explicit_counter_loop::check(cx, pat, arg, body, expr, label); explicit_counter_loop::check(cx, pat, arg, body, expr, label);
} }
@ -858,8 +856,8 @@ impl Loops {
for_kv_map::check(cx, pat, arg, body); for_kv_map::check(cx, pat, arg, body);
mut_range_bound::check(cx, arg, body); mut_range_bound::check(cx, arg, body);
single_element_loop::check(cx, pat, arg, body, expr); single_element_loop::check(cx, pat, arg, body, expr);
same_item_push::check(cx, pat, arg, body, expr, &self.msrv); same_item_push::check(cx, pat, arg, body, expr, self.msrv);
manual_flatten::check(cx, pat, arg, body, span); manual_flatten::check(cx, pat, arg, body, span, self.msrv);
manual_find::check(cx, pat, arg, body, span, expr); manual_find::check(cx, pat, arg, body, span, expr);
unused_enumerate_index::check(cx, pat, arg, body); unused_enumerate_index::check(cx, pat, arg, body);
} }
@ -868,7 +866,7 @@ impl Loops {
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
match method.ident.as_str() { match method.ident.as_str() {
"iter" | "iter_mut" => { "iter" | "iter_mut" => {
explicit_iter_loop::check(cx, self_arg, arg, &self.msrv, self.enforce_iter_loop_reborrow); explicit_iter_loop::check(cx, self_arg, arg, self.msrv, self.enforce_iter_loop_reborrow);
}, },
"into_iter" => { "into_iter" => {
explicit_into_iter_loop::check(cx, self_arg, arg); explicit_into_iter_loop::check(cx, self_arg, arg);

View file

@ -20,14 +20,14 @@ pub(super) fn check<'tcx>(
_: &'tcx Expr<'_>, _: &'tcx Expr<'_>,
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>, _: &'tcx Expr<'_>,
msrv: &Msrv, msrv: Msrv,
) { ) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) { fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: Msrv) {
let mut app = Applicability::Unspecified; let mut app = Applicability::Unspecified;
let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0; let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0; let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
let secondary_help = if msrv.meets(msrvs::REPEAT_N) let secondary_help = if msrv.meets(cx, msrvs::REPEAT_N)
&& let Some(std_or_core) = std_or_core(cx) && let Some(std_or_core) = std_or_core(cx)
{ {
format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`") format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`")

View file

@ -21,7 +21,21 @@ declare_clippy_lint! {
/// ### Example /// ### Example
/// ```rust,ignore /// ```rust,ignore
/// #[macro_use] /// #[macro_use]
/// use some_macro; /// extern crate some_crate;
///
/// fn main() {
/// some_macro!();
/// }
/// ```
///
/// Use instead:
///
/// ```rust,ignore
/// use some_crate::some_macro;
///
/// fn main() {
/// some_macro!();
/// }
/// ``` /// ```
#[clippy::version = "1.44.0"] #[clippy::version = "1.44.0"]
pub MACRO_USE_IMPORTS, pub MACRO_USE_IMPORTS,
@ -103,11 +117,6 @@ impl LateLintPass<'_> for MacroUseImports {
self.push_unique_macro_pat_ty(cx, item.span); self.push_unique_macro_pat_ty(cx, item.span);
} }
} }
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) {
if attr.span().from_expansion() {
self.push_unique_macro(cx, attr.span());
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
if expr.span.from_expansion() { if expr.span.from_expansion() {
self.push_unique_macro(cx, expr.span); self.push_unique_macro(cx, expr.span);

View file

@ -62,6 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
&& let Some(closure_body) = desugared_async_block(cx, block) && let Some(closure_body) = desugared_async_block(cx, block)
&& let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
cx.tcx.hir_node_by_def_id(fn_def_id) cx.tcx.hir_node_by_def_id(fn_def_id)
&& !span.from_expansion()
{ {
let header_span = span.with_hi(ret_ty.span.hi()); let header_span = span.with_hi(ret_ty.span.hi());

View file

@ -40,9 +40,7 @@ pub struct ManualBits {
impl ManualBits { impl ManualBits {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -53,7 +51,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
&& let BinOpKind::Mul = &bin_op.node && let BinOpKind::Mul = &bin_op.node
&& !expr.span.from_expansion() && !expr.span.from_expansion()
&& self.msrv.meets(msrvs::MANUAL_BITS)
&& let ctxt = expr.span.ctxt() && let ctxt = expr.span.ctxt()
&& left_expr.span.ctxt() == ctxt && left_expr.span.ctxt() == ctxt
&& right_expr.span.ctxt() == ctxt && right_expr.span.ctxt() == ctxt
@ -61,6 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
&& matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)) && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_))
&& let ExprKind::Lit(lit) = &other_expr.kind && let ExprKind::Lit(lit) = &other_expr.kind
&& let LitKind::Int(Pu128(8), _) = lit.node && let LitKind::Int(Pu128(8), _) = lit.node
&& self.msrv.meets(cx, msrvs::INTEGER_BITS)
{ {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let ty_snip = snippet_with_context(cx, real_ty_span, ctxt, "..", &mut app).0; let ty_snip = snippet_with_context(cx, real_ty_span, ctxt, "..", &mut app).0;
@ -77,8 +75,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
fn get_one_size_of_ty<'tcx>( fn get_one_size_of_ty<'tcx>(

View file

@ -99,9 +99,7 @@ pub struct ManualClamp {
impl ManualClamp { impl ManualClamp {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -144,30 +142,28 @@ struct InputMinMax<'tcx> {
impl<'tcx> LateLintPass<'tcx> for ManualClamp { impl<'tcx> LateLintPass<'tcx> for ManualClamp {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if !self.msrv.meets(msrvs::CLAMP) {
return;
}
if !expr.span.from_expansion() && !is_in_const_context(cx) { if !expr.span.from_expansion() && !is_in_const_context(cx) {
let suggestion = is_if_elseif_else_pattern(cx, expr) let suggestion = is_if_elseif_else_pattern(cx, expr)
.or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_max_min_pattern(cx, expr))
.or_else(|| is_call_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr))
.or_else(|| is_match_pattern(cx, expr)) .or_else(|| is_match_pattern(cx, expr))
.or_else(|| is_if_elseif_pattern(cx, expr)); .or_else(|| is_if_elseif_pattern(cx, expr));
if let Some(suggestion) = suggestion { if let Some(suggestion) = suggestion
&& self.msrv.meets(cx, msrvs::CLAMP)
{
maybe_emit_suggestion(cx, &suggestion); maybe_emit_suggestion(cx, &suggestion);
} }
} }
} }
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
if !self.msrv.meets(msrvs::CLAMP) || is_in_const_context(cx) { if is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::CLAMP) {
return; return;
} }
for suggestion in is_two_if_pattern(cx, block) { for suggestion in is_two_if_pattern(cx, block) {
maybe_emit_suggestion(cx, &suggestion); maybe_emit_suggestion(cx, &suggestion);
} }
} }
extract_msrv_attr!(LateContext);
} }
fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) {

View file

@ -49,9 +49,7 @@ pub struct ManualDivCeil {
impl ManualDivCeil { impl ManualDivCeil {
#[must_use] #[must_use]
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -59,10 +57,6 @@ impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]);
impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) {
return;
}
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind
@ -70,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
&& check_int_ty_and_feature(cx, div_lhs) && check_int_ty_and_feature(cx, div_lhs)
&& check_int_ty_and_feature(cx, div_rhs) && check_int_ty_and_feature(cx, div_rhs)
&& let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind
&& self.msrv.meets(cx, msrvs::MANUAL_DIV_CEIL)
{ {
// (x + (y - 1)) / y // (x + (y - 1)) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
@ -122,8 +117,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 == /// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 ==

View file

@ -90,9 +90,7 @@ pub struct ManualFloatMethods {
impl ManualFloatMethods { impl ManualFloatMethods {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -144,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
&& !expr.span.in_external_macro(cx.sess().source_map()) && !expr.span.in_external_macro(cx.sess().source_map())
&& ( && (
is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into()) is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into())
|| self.msrv.meets(msrvs::CONST_FLOAT_CLASSIFY) || self.msrv.meets(cx, msrvs::CONST_FLOAT_CLASSIFY)
) )
&& let [first, second, const_1, const_2] = exprs && let [first, second, const_1, const_2] = exprs
&& let ecx = ConstEvalCtxt::new(cx) && let ecx = ConstEvalCtxt::new(cx)
@ -202,8 +200,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
}); });
} }
} }
extract_msrv_attr!(LateContext);
} }
fn is_infinity(constant: &Constant<'_>) -> bool { fn is_infinity(constant: &Constant<'_>) -> bool {

View file

@ -53,9 +53,7 @@ pub struct ManualHashOne {
impl ManualHashOne { impl ManualHashOne {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -98,7 +96,7 @@ impl LateLintPass<'_> for ManualHashOne {
&& let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind
&& seg.ident.name.as_str() == "finish" && seg.ident.name.as_str() == "finish"
&& self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE) && self.msrv.meets(cx, msrvs::BUILD_HASHER_HASH_ONE)
{ {
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
@ -129,6 +127,4 @@ impl LateLintPass<'_> for ManualHashOne {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -64,9 +64,7 @@ pub struct ManualIsAsciiCheck {
impl ManualIsAsciiCheck { impl ManualIsAsciiCheck {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -91,11 +89,11 @@ enum CharRange {
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) { if !self.msrv.meets(cx, msrvs::IS_ASCII_DIGIT) {
return; return;
} }
if is_in_const_context(cx) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { if is_in_const_context(cx) && !self.msrv.meets(cx, msrvs::IS_ASCII_DIGIT_CONST) {
return; return;
} }
@ -119,8 +117,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
check_is_ascii(cx, expr.span, arg, &range, ty_sugg); check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
} }
} }
extract_msrv_attr!(LateContext);
} }
fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> {

View file

@ -53,8 +53,8 @@ impl<'tcx> QuestionMark {
&& local.ty.is_none() && local.ty.is_none()
&& init.span.eq_ctxt(stmt.span) && init.span.eq_ctxt(stmt.span)
&& let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) && let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
&& self.msrv.meets(msrvs::LET_ELSE)
&& !stmt.span.in_external_macro(cx.sess().source_map()) && !stmt.span.in_external_macro(cx.sess().source_map())
&& self.msrv.meets(cx, msrvs::LET_ELSE)
{ {
match if_let_or_match { match if_let_or_match {
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else, ..) => { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else, ..) => {

View file

@ -39,9 +39,7 @@ pub struct ManualMainSeparatorStr {
impl ManualMainSeparatorStr { impl ManualMainSeparatorStr {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -55,10 +53,10 @@ impl LateLintPass<'_> for ManualMainSeparatorStr {
&& let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind
&& let Res::Def(DefKind::Const, receiver_def_id) = path.res && let Res::Def(DefKind::Const, receiver_def_id) = path.res
&& is_trait_method(cx, target, sym::ToString) && is_trait_method(cx, target, sym::ToString)
&& self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
&& cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id)
&& let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind()
&& ty.is_str() && ty.is_str()
&& self.msrv.meets(cx, msrvs::PATH_MAIN_SEPARATOR_STR)
{ {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -71,6 +69,4 @@ impl LateLintPass<'_> for ManualMainSeparatorStr {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -71,7 +71,7 @@ pub struct ManualNonExhaustive {
impl ManualNonExhaustive { impl ManualNonExhaustive {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
msrv: conf.msrv.clone(), msrv: conf.msrv,
constructed_enum_variants: FxHashSet::default(), constructed_enum_variants: FxHashSet::default(),
potential_enums: Vec::new(), potential_enums: Vec::new(),
} }
@ -82,7 +82,7 @@ impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) { if !cx.effective_visibilities.is_exported(item.owner_id.def_id) || !self.msrv.meets(cx, msrvs::NON_EXHAUSTIVE) {
return; return;
} }
@ -171,6 +171,4 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -1,5 +1,6 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::msrvs::Msrv;
use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -40,31 +41,21 @@ declare_clippy_lint! {
} }
pub struct ManualOptionAsSlice { pub struct ManualOptionAsSlice {
msrv: msrvs::Msrv, msrv: Msrv,
} }
impl ManualOptionAsSlice { impl ManualOptionAsSlice {
pub fn new(conf: &Conf) -> Self { pub fn new(conf: &Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
impl_lint_pass!(ManualOptionAsSlice => [MANUAL_OPTION_AS_SLICE]); impl_lint_pass!(ManualOptionAsSlice => [MANUAL_OPTION_AS_SLICE]);
impl LateLintPass<'_> for ManualOptionAsSlice { impl LateLintPass<'_> for ManualOptionAsSlice {
extract_msrv_attr!(LateContext);
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let span = expr.span; let span = expr.span;
if span.from_expansion() if span.from_expansion() {
|| !self.msrv.meets(if clippy_utils::is_in_const_context(cx) {
msrvs::CONST_OPTION_AS_SLICE
} else {
msrvs::OPTION_AS_SLICE
})
{
return; return;
} }
match expr.kind { match expr.kind {
@ -72,7 +63,7 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1)
|| is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2)
{ {
check_as_ref(cx, scrutinee, span); check_as_ref(cx, scrutinee, span, self.msrv);
} }
}, },
ExprKind::If(cond, then, Some(other)) => { ExprKind::If(cond, then, Some(other)) => {
@ -81,23 +72,23 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
&& check_some_body(cx, binding, then) && check_some_body(cx, binding, then)
&& is_empty_slice(cx, other.peel_blocks()) && is_empty_slice(cx, other.peel_blocks())
{ {
check_as_ref(cx, let_expr.init, span); check_as_ref(cx, let_expr.init, span, self.msrv);
} }
}, },
ExprKind::MethodCall(seg, callee, [], _) => { ExprKind::MethodCall(seg, callee, [], _) => {
if seg.ident.name.as_str() == "unwrap_or_default" { if seg.ident.name.as_str() == "unwrap_or_default" {
check_map(cx, callee, span); check_map(cx, callee, span, self.msrv);
} }
}, },
ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() {
"unwrap_or" => { "unwrap_or" => {
if is_empty_slice(cx, or) { if is_empty_slice(cx, or) {
check_map(cx, callee, span); check_map(cx, callee, span, self.msrv);
} }
}, },
"unwrap_or_else" => { "unwrap_or_else" => {
if returns_empty_slice(cx, or) { if returns_empty_slice(cx, or) {
check_map(cx, callee, span); check_map(cx, callee, span, self.msrv);
} }
}, },
_ => {}, _ => {},
@ -105,12 +96,12 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() {
"map_or" => { "map_or" => {
if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
check_as_ref(cx, callee, span); check_as_ref(cx, callee, span, self.msrv);
} }
}, },
"map_or_else" => { "map_or_else" => {
if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
check_as_ref(cx, callee, span); check_as_ref(cx, callee, span, self.msrv);
} }
}, },
_ => {}, _ => {},
@ -120,20 +111,28 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
} }
} }
fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span) { fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span, msrv: Msrv) {
if let ExprKind::MethodCall(seg, callee, [mapping], _) = map.kind if let ExprKind::MethodCall(seg, callee, [mapping], _) = map.kind
&& seg.ident.name == sym::map && seg.ident.name == sym::map
&& is_slice_from_ref(cx, mapping) && is_slice_from_ref(cx, mapping)
{ {
check_as_ref(cx, callee, span); check_as_ref(cx, callee, span, msrv);
} }
} }
fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span) { fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) {
if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind
&& seg.ident.name == sym::as_ref && seg.ident.name == sym::as_ref
&& let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind() && let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind()
&& cx.tcx.is_diagnostic_item(sym::Option, adtdef.did()) && cx.tcx.is_diagnostic_item(sym::Option, adtdef.did())
&& msrv.meets(
cx,
if clippy_utils::is_in_const_context(cx) {
msrvs::CONST_OPTION_AS_SLICE
} else {
msrvs::OPTION_AS_SLICE
},
)
{ {
if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) {
span_lint_and_sugg( span_lint_and_sugg(

View file

@ -39,9 +39,7 @@ pub struct ManualRemEuclid {
impl ManualRemEuclid { impl ManualRemEuclid {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -60,8 +58,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
&& add_lhs.span.ctxt() == ctxt && add_lhs.span.ctxt() == ctxt
&& add_rhs.span.ctxt() == ctxt && add_rhs.span.ctxt() == ctxt
&& !expr.span.in_external_macro(cx.sess().source_map()) && !expr.span.in_external_macro(cx.sess().source_map())
&& self.msrv.meets(msrvs::REM_EUCLID)
&& (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx))
&& let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
&& let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
@ -73,6 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
&& const2 == const3 && const2 == const3
&& rem2_lhs.span.ctxt() == ctxt && rem2_lhs.span.ctxt() == ctxt
&& rem2_rhs.span.ctxt() == ctxt && rem2_rhs.span.ctxt() == ctxt
&& self.msrv.meets(cx, msrvs::REM_EUCLID)
&& (self.msrv.meets(cx, msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx))
{ {
// Apply only to params or locals with annotated types // Apply only to params or locals with annotated types
match cx.tcx.parent_hir_node(hir_id) { match cx.tcx.parent_hir_node(hir_id) {
@ -99,8 +97,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
); );
} }
} }
extract_msrv_attr!(LateContext);
} }
// Checks if either the left or right expressions can be an unsigned int constant and returns that // Checks if either the left or right expressions can be an unsigned int constant and returns that

View file

@ -50,9 +50,7 @@ pub struct ManualRetain {
impl ManualRetain { impl ManualRetain {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -66,13 +64,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
&& cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
{ {
check_into_iter(cx, left_expr, target_expr, expr.span, &self.msrv); check_into_iter(cx, left_expr, target_expr, expr.span, self.msrv);
check_iter(cx, left_expr, target_expr, expr.span, &self.msrv); check_iter(cx, left_expr, target_expr, expr.span, self.msrv);
check_to_owned(cx, left_expr, target_expr, expr.span, &self.msrv); check_to_owned(cx, left_expr, target_expr, expr.span, self.msrv);
} }
} }
extract_msrv_attr!(LateContext);
} }
fn check_into_iter( fn check_into_iter(
@ -80,7 +76,7 @@ fn check_into_iter(
left_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>,
parent_expr_span: Span, parent_expr_span: Span,
msrv: &Msrv, msrv: Msrv,
) { ) {
if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@ -123,7 +119,7 @@ fn check_iter(
left_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>,
parent_expr_span: Span, parent_expr_span: Span,
msrv: &Msrv, msrv: Msrv,
) { ) {
if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@ -181,10 +177,9 @@ fn check_to_owned(
left_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>,
parent_expr_span: Span, parent_expr_span: Span,
msrv: &Msrv, msrv: Msrv,
) { ) {
if msrv.meets(msrvs::STRING_RETAIN) if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
&& cx.tcx.is_diagnostic_item(sym::to_owned_method, to_owned_def_id) && cx.tcx.is_diagnostic_item(sym::to_owned_method, to_owned_def_id)
&& let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind && let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind
@ -200,6 +195,7 @@ fn check_to_owned(
&& let hir::ExprKind::Closure(closure) = closure_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind
&& let filter_body = cx.tcx.hir_body(closure.body) && let filter_body = cx.tcx.hir_body(closure.body)
&& let [filter_params] = filter_body.params && let [filter_params] = filter_body.params
&& msrv.meets(cx, msrvs::STRING_RETAIN)
{ {
if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind {
make_span_lint_and_sugg( make_span_lint_and_sugg(
@ -253,7 +249,7 @@ fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
.any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id)) .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id))
} }
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool {
let ty = cx.typeck_results().expr_ty(expr).peel_refs(); let ty = cx.typeck_results().expr_ty(expr).peel_refs();
let required = match get_type_diagnostic_name(cx, ty) { let required = match get_type_diagnostic_name(cx, ty) {
Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
@ -264,7 +260,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv
Some(sym::Vec | sym::VecDeque) => return true, Some(sym::Vec | sym::VecDeque) => return true,
_ => return false, _ => return false,
}; };
msrv.meets(required) msrv.meets(cx, required)
} }
fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {

View file

@ -2,19 +2,21 @@ use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
use clippy_utils::{eq_expr_value, higher}; use clippy_utils::{eq_expr_value, higher};
use rustc_ast::BindingMode;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat};
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass, LintContext as _};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::{Span, sym}; use rustc_span::{Symbol, sym};
use std::iter; use std::iter;
declare_clippy_lint! { declare_clippy_lint! {
@ -54,9 +56,7 @@ pub struct ManualStrip {
impl ManualStrip { impl ManualStrip {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self { msrv: conf.msrv }
msrv: conf.msrv.clone(),
}
} }
} }
@ -73,7 +73,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
&& let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind
&& let ExprKind::Path(target_path) = &target_arg.kind && let ExprKind::Path(target_path) = &target_arg.kind
&& self.msrv.meets(msrvs::STR_STRIP_PREFIX)
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
{ {
let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) { let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) {
@ -95,18 +94,37 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
return; return;
} }
let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then);
if !strippings.is_empty() { if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) {
let kind_word = match strip_kind { let kind_word = match strip_kind {
StripKind::Prefix => "prefix", StripKind::Prefix => "prefix",
StripKind::Suffix => "suffix", StripKind::Suffix => "suffix",
}; };
let test_span = expr.span.until(then.span); let test_span = expr.span.until(then.span);
// If the first use is a simple `let` statement, reuse its identifier in the `if let Some(…)` and
// remove the `let` statement as long as the identifier is never bound again within the lexical
// scope of interest.
let (ident_name, let_stmt_span, skip, mut app) = if let Node::LetStmt(let_stmt) =
cx.tcx.parent_hir_node(strippings[0].hir_id)
&& let PatKind::Binding(BindingMode::NONE, _, ident, None) = &let_stmt.pat.kind
&& bindings.get(&ident.name) == Some(&1)
{
(
ident.name.as_str(),
Some(cx.sess().source_map().span_extend_while_whitespace(let_stmt.span)),
1,
Applicability::MachineApplicable,
)
} else {
("<stripped>", None, 0, Applicability::HasPlaceholders)
};
span_lint_and_then( span_lint_and_then(
cx, cx,
MANUAL_STRIP, MANUAL_STRIP,
strippings[0], strippings[0].span,
format!("stripping a {kind_word} manually"), format!("stripping a {kind_word} manually"),
|diag| { |diag| {
diag.span_note(test_span, format!("the {kind_word} was tested here")); diag.span_note(test_span, format!("the {kind_word} was tested here"));
@ -115,22 +133,26 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
iter::once(( iter::once((
test_span, test_span,
format!( format!(
"if let Some(<stripped>) = {}.strip_{kind_word}({}) ", "if let Some({ident_name}) = {}.strip_{kind_word}({}) ",
snippet(cx, target_arg.span, ".."), snippet_with_applicability(cx, target_arg.span, "_", &mut app),
snippet(cx, pattern.span, "..") snippet_with_applicability(cx, pattern.span, "_", &mut app)
), ),
)) ))
.chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))) .chain(let_stmt_span.map(|span| (span, String::new())))
.chain(
strippings
.into_iter()
.skip(skip)
.map(|expr| (expr.span, ident_name.into())),
)
.collect(), .collect(),
Applicability::HasPlaceholders, app,
); );
}, },
); );
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
@ -188,19 +210,21 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
/// Find expressions where `target` is stripped using the length of `pattern`. /// Find expressions where `target` is stripped using the length of `pattern`.
/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` /// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
/// method. /// method.
/// Also, all bindings found during the visit are counted and returned.
fn find_stripping<'tcx>( fn find_stripping<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
strip_kind: StripKind, strip_kind: StripKind,
target: Res, target: Res,
pattern: &'tcx Expr<'_>, pattern: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'tcx>,
) -> Vec<Span> { ) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap<Symbol, usize>) {
struct StrippingFinder<'a, 'tcx> { struct StrippingFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
strip_kind: StripKind, strip_kind: StripKind,
target: Res, target: Res,
pattern: &'tcx Expr<'tcx>, pattern: &'tcx Expr<'tcx>,
results: Vec<Span>, results: Vec<&'tcx Expr<'tcx>>,
bindings: FxHashMap<Symbol, usize>,
} }
impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> {
@ -215,7 +239,7 @@ fn find_stripping<'tcx>(
match (self.strip_kind, start, end) { match (self.strip_kind, start, end) {
(StripKind::Prefix, Some(start), None) => { (StripKind::Prefix, Some(start), None) => {
if eq_pattern_length(self.cx, self.pattern, start) { if eq_pattern_length(self.cx, self.pattern, start) {
self.results.push(ex.span); self.results.push(ex);
return; return;
} }
}, },
@ -232,7 +256,7 @@ fn find_stripping<'tcx>(
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
&& eq_pattern_length(self.cx, self.pattern, right) && eq_pattern_length(self.cx, self.pattern, right)
{ {
self.results.push(ex.span); self.results.push(ex);
return; return;
} }
}, },
@ -242,6 +266,13 @@ fn find_stripping<'tcx>(
walk_expr(self, ex); walk_expr(self, ex);
} }
fn visit_pat(&mut self, pat: &'tcx rustc_hir::Pat<'tcx>) -> Self::Result {
if let PatKind::Binding(_, _, ident, _) = pat.kind {
*self.bindings.entry(ident.name).or_default() += 1;
}
walk_pat(self, pat);
}
} }
let mut finder = StrippingFinder { let mut finder = StrippingFinder {
@ -250,7 +281,8 @@ fn find_stripping<'tcx>(
target, target,
pattern, pattern,
results: vec![], results: vec![],
bindings: FxHashMap::default(),
}; };
walk_expr(&mut finder, expr); walk_expr(&mut finder, expr);
finder.results (finder.results, finder.bindings)
} }

View file

@ -14,7 +14,7 @@ use rustc_span::Span;
use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) { pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
for arm in arms { for arm in arms {
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv); check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
@ -27,7 +27,7 @@ pub(super) fn check_if_let<'tcx>(
pat: &'tcx Pat<'_>, pat: &'tcx Pat<'_>,
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
else_expr: Option<&'tcx Expr<'_>>, else_expr: Option<&'tcx Expr<'_>>,
msrv: &Msrv, msrv: Msrv,
) { ) {
check_arm(cx, false, pat, body, None, else_expr, msrv); check_arm(cx, false, pat, body, None, else_expr, msrv);
} }
@ -39,7 +39,7 @@ fn check_arm<'tcx>(
outer_then_body: &'tcx Expr<'tcx>, outer_then_body: &'tcx Expr<'tcx>,
outer_guard: Option<&'tcx Expr<'tcx>>, outer_guard: Option<&'tcx Expr<'tcx>>,
outer_else_body: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>,
msrv: &Msrv, msrv: Msrv,
) { ) {
let inner_expr = peel_blocks_with_stmt(outer_then_body); let inner_expr = peel_blocks_with_stmt(outer_then_body);
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
@ -60,7 +60,7 @@ fn check_arm<'tcx>(
// match expression must be a local binding // match expression must be a local binding
// match <local> { .. } // match <local> { .. }
&& let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
&& !pat_contains_disallowed_or(inner_then_pat, msrv) && !pat_contains_disallowed_or(cx, inner_then_pat, msrv)
// the binding must come from the pattern of the containing match arm // the binding must come from the pattern of the containing match arm
// ..<local>.. => match <local> { .. } // ..<local>.. => match <local> { .. }
&& let (Some(binding_span), is_innermost_parent_pat_struct) && let (Some(binding_span), is_innermost_parent_pat_struct)

View file

@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::option_arg_ty; use clippy_utils::ty::option_arg_ty;
use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment};
use rustc_ast::BindingMode; use rustc_ast::BindingMode;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
@ -132,13 +133,23 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok
Applicability::MachineApplicable Applicability::MachineApplicable
}; };
let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par();
let sugg = format!("{scrut}.{method}()");
// If the expression being expanded is the `if …` part of an `else if …`, it must be blockified.
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::If(_, _, Some(else_part)) = parent_expr.kind
&& else_part.hir_id == expr.hir_id
{
reindent_multiline(&format!("{{\n {sugg}\n}}"), true, indent_of(cx, parent_expr.span))
} else {
sugg
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MANUAL_OK_ERR, MANUAL_OK_ERR,
expr.span, expr.span,
format!("manual implementation of `{method}`"), format!("manual implementation of `{method}`"),
"replace with", "replace with",
format!("{scrut}.{method}()"), sugg,
app, app,
); );
} }

View file

@ -117,7 +117,7 @@ fn lint<'tcx>(
or_body_snippet: &str, or_body_snippet: &str,
indent: usize, indent: usize,
) { ) {
let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); let reindented_or_body = reindent_multiline(or_body_snippet, true, Some(indent));
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();

View file

@ -4,8 +4,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
use clippy_utils::{ use clippy_utils::{
CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor,
path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
}; };
use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -73,7 +73,7 @@ where
} }
// `map` won't perform any adjustments. // `map` won't perform any adjustments.
if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { if expr_requires_coercion(cx, expr) {
return None; return None;
} }
@ -124,6 +124,12 @@ where
}; };
let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app);
let closure_body = if some_expr.needs_unsafe_block {
format!("unsafe {}", closure_expr_snip.blockify())
} else {
closure_expr_snip.to_string()
};
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
if !some_expr.needs_unsafe_block if !some_expr.needs_unsafe_block
&& let Some(func) = can_pass_as_func(cx, id, some_expr.expr) && let Some(func) = can_pass_as_func(cx, id, some_expr.expr)
@ -145,20 +151,12 @@ where
"" ""
}; };
if some_expr.needs_unsafe_block { format!("|{annotation}{some_binding}| {closure_body}")
format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}")
} else {
format!("|{annotation}{some_binding}| {closure_expr_snip}")
}
} }
} else if !is_wild_none && explicit_ref.is_none() { } else if !is_wild_none && explicit_ref.is_none() {
// TODO: handle explicit reference annotations. // TODO: handle explicit reference annotations.
let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
if some_expr.needs_unsafe_block { format!("|{pat_snip}| {closure_body}")
format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}")
} else {
format!("|{pat_snip}| {closure_expr_snip}")
}
} else { } else {
// Refutable bindings and mixed reference annotations can't be handled by `map`. // Refutable bindings and mixed reference annotations can't be handled by `map`.
return None; return None;

Some files were not shown because too many files have changed in this diff Show more