Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
02e812af4d
2562 changed files with 34500 additions and 15442 deletions
57
CHANGELOG.md
57
CHANGELOG.md
|
|
@ -6,11 +6,53 @@ document.
|
|||
|
||||
## 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
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -5530,6 +5572,7 @@ Released 2018-09-13
|
|||
[`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_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_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
|
||||
|
|
@ -5549,6 +5592,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`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
|
||||
[`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
|
||||
|
|
@ -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
|
||||
[`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
|
||||
[`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
|
||||
[`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
|
||||
|
|
@ -5742,6 +5787,7 @@ Released 2018-09-13
|
|||
[`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_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_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
|
||||
|
|
@ -5812,6 +5858,7 @@ Released 2018-09-13
|
|||
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
|
||||
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
|
||||
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
|
||||
[`mem_replace_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_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
|
||||
|
|
@ -5939,6 +5986,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`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_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
|
||||
|
|
@ -6067,6 +6115,7 @@ Released 2018-09-13
|
|||
[`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_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
|
||||
[`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
|
||||
|
|
@ -6143,6 +6192,7 @@ Released 2018-09-13
|
|||
[`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_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
|
||||
[`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
|
||||
|
|
@ -6161,6 +6211,7 @@ Released 2018-09-13
|
|||
[`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_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_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
|
||||
|
|
@ -6267,6 +6318,7 @@ Released 2018-09-13
|
|||
[`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-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-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
|
||||
|
|
@ -6275,6 +6327,7 @@ Released 2018-09-13
|
|||
[`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-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-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
|
||||
|
|
|
|||
13
Cargo.toml
13
Cargo.toml
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
# begin autogenerated version
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
# end autogenerated version
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
@ -25,7 +25,7 @@ path = "src/driver.rs"
|
|||
[dependencies]
|
||||
clippy_config = { path = "clippy_config" }
|
||||
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 }
|
||||
termize = "0.1"
|
||||
color-print = "0.3.4"
|
||||
|
|
@ -54,7 +54,7 @@ parking_lot = "0.12"
|
|||
tokio = { version = "1", features = ["io-util"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = "0.4.0"
|
||||
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
|
||||
|
||||
[features]
|
||||
integration = ["tempfile"]
|
||||
|
|
@ -71,3 +71,10 @@ harness = false
|
|||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
|||
|
|
@ -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
|
||||
the tests.
|
||||
|
||||
[rustfix]: https://github.com/rust-lang/rustfix
|
||||
[rustfix]: https://github.com/rust-lang/cargo/tree/master/crates/rustfix
|
||||
|
||||
## Testing manually
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
> 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`
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
|
||||
## Testing Manually
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
## `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`
|
||||
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:**
|
||||
* [`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`
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
## `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`
|
||||
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)
|
||||
* [`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)
|
||||
* [`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_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)
|
||||
|
|
@ -738,15 +759,20 @@ 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)
|
||||
* [`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)
|
||||
* [`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)
|
||||
* [`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_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_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_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_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_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)
|
||||
|
|
@ -761,6 +787,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_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)
|
||||
* [`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)
|
||||
* [`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)
|
||||
|
|
@ -770,6 +797,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)
|
||||
* [`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)
|
||||
* [`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)
|
||||
* [`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)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_config"
|
||||
# begin autogenerated version
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
# end autogenerated version
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::ClippyConfiguration;
|
||||
use crate::types::{
|
||||
DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering,
|
||||
SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind,
|
||||
SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
|
||||
DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour,
|
||||
Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings,
|
||||
SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
|
||||
};
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -288,6 +288,9 @@ define_Conf! {
|
|||
/// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
|
||||
#[lints(dbg_macro)]
|
||||
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)]`
|
||||
#[lints(expect_used)]
|
||||
allow_expect_in_tests: bool = false,
|
||||
|
|
@ -298,7 +301,7 @@ define_Conf! {
|
|||
#[lints(uninlined_format_args)]
|
||||
allow_mixed_uninlined_format_args: bool = true,
|
||||
/// 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,
|
||||
/// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
|
||||
#[lints(panic)]
|
||||
|
|
@ -325,6 +328,9 @@ define_Conf! {
|
|||
#[lints(renamed_function_params)]
|
||||
allow_renamed_params_for: Vec<String> =
|
||||
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)]`
|
||||
#[lints(unwrap_used)]
|
||||
allow_unwrap_in_tests: bool = false,
|
||||
|
|
@ -432,6 +438,7 @@ define_Conf! {
|
|||
linkedlist,
|
||||
needless_pass_by_ref_mut,
|
||||
option_option,
|
||||
owned_cow,
|
||||
rc_buffer,
|
||||
rc_mutex,
|
||||
redundant_allocation,
|
||||
|
|
@ -448,7 +455,7 @@ define_Conf! {
|
|||
avoid_breaking_exported_api: bool = true,
|
||||
/// The list of types which may not be held across an await point.
|
||||
#[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.
|
||||
///
|
||||
/// Use the Disallowed Names lint instead
|
||||
|
|
@ -607,15 +614,20 @@ define_Conf! {
|
|||
from_over_into,
|
||||
if_then_some_else_none,
|
||||
index_refutable_slice,
|
||||
io_other_error,
|
||||
iter_kv_map,
|
||||
legacy_numeric_constants,
|
||||
lines_filter_map_ok,
|
||||
manual_bits,
|
||||
manual_c_str_literals,
|
||||
manual_clamp,
|
||||
manual_div_ceil,
|
||||
manual_flatten,
|
||||
manual_hash_one,
|
||||
manual_is_ascii_check,
|
||||
manual_let_else,
|
||||
manual_non_exhaustive,
|
||||
manual_option_as_slice,
|
||||
manual_pattern_char_comparison,
|
||||
manual_range_contains,
|
||||
manual_rem_euclid,
|
||||
|
|
@ -630,6 +642,7 @@ define_Conf! {
|
|||
map_unwrap_or,
|
||||
map_with_unused_argument_over_ranges,
|
||||
match_like_matches_macro,
|
||||
mem_replace_option_with_some,
|
||||
mem_replace_with_default,
|
||||
missing_const_for_fn,
|
||||
needless_borrow,
|
||||
|
|
@ -639,6 +652,7 @@ define_Conf! {
|
|||
ptr_as_ptr,
|
||||
redundant_field_names,
|
||||
redundant_static_lifetimes,
|
||||
repeat_vec_with_capacity,
|
||||
same_item_push,
|
||||
seek_from_current,
|
||||
seek_rewind,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use clippy_utils::def_path_def_ids;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::Span;
|
||||
use serde::de::{self, Deserializer, Visitor};
|
||||
use serde::{Deserialize, Serialize, ser};
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -12,37 +14,99 @@ pub struct Rename {
|
|||
pub rename: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DisallowedPath {
|
||||
Simple(String),
|
||||
WithReason { path: String, reason: Option<String> },
|
||||
pub type DisallowedPathWithoutReplacement = DisallowedPath<false>;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
|
||||
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 {
|
||||
let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
pub fn reason(&self) -> Option<&str> {
|
||||
fn reason(&self) -> Option<&str> {
|
||||
match &self {
|
||||
Self::WithReason { reason, .. } => reason.as_deref(),
|
||||
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.
|
||||
pub fn create_disallowed_map(
|
||||
pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
|
||||
tcx: TyCtxt<'_>,
|
||||
disallowed: &'static [DisallowedPath],
|
||||
) -> DefIdMap<(&'static str, Option<&'static str>)> {
|
||||
disallowed: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
|
||||
) -> DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> {
|
||||
disallowed
|
||||
.iter()
|
||||
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason()))
|
||||
.flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason))))
|
||||
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
|
||||
.flat_map(|(name, path, disallowed_path)| {
|
||||
def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
@ -436,7 +500,6 @@ macro_rules! unimplemented_serialize {
|
|||
}
|
||||
|
||||
unimplemented_serialize! {
|
||||
DisallowedPath,
|
||||
Rename,
|
||||
MacroMatcher,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ use std::process::Command;
|
|||
/// # Panics
|
||||
///
|
||||
/// 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");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if allow_no_vcs {
|
||||
dogfood_args.push("--allow-no-vcs");
|
||||
}
|
||||
|
||||
cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
|
||||
|
||||
exit_if_err(cmd.status());
|
||||
|
|
|
|||
|
|
@ -290,8 +290,13 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
|
|||
.filter_map(|entry| {
|
||||
let entry = entry.expect("failed to find tests");
|
||||
let path = entry.path();
|
||||
|
||||
if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
|
||||
if path.extension() != Some("rs".as_ref())
|
||||
|| path
|
||||
.components()
|
||||
.nth_back(1)
|
||||
.is_some_and(|c| c.as_os_str() == "syntax-error-recovery")
|
||||
|| entry.file_name() == "ice-3891.rs"
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(entry.into_path().into_os_string())
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::utils::{cargo_clippy_path, exit_if_err};
|
|||
use std::process::{self, Command};
|
||||
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) {
|
||||
Ok(metadata) => metadata.is_file(),
|
||||
Err(e) => {
|
||||
|
|
@ -17,7 +17,7 @@ pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
|
|||
.args(["run", "--bin", "clippy-driver", "--"])
|
||||
.args(["-L", "./target/debug"])
|
||||
.args(["-Z", "no-codegen"])
|
||||
.args(["--edition", "2021"])
|
||||
.args(["--edition", edition])
|
||||
.arg(path)
|
||||
.args(args)
|
||||
// Prevent rustc from creating `rustc-ice-*` files the console output is enough.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ fn main() {
|
|||
fix,
|
||||
allow_dirty,
|
||||
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::UpdateLints { print_only, check } => {
|
||||
if print_only {
|
||||
|
|
@ -53,7 +54,12 @@ fn main() {
|
|||
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 } => {
|
||||
if remove {
|
||||
setup::vscode::remove_tasks();
|
||||
|
|
@ -68,7 +74,7 @@ fn main() {
|
|||
RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
|
||||
},
|
||||
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 {
|
||||
old_name,
|
||||
new_name,
|
||||
|
|
@ -106,6 +112,9 @@ enum DevCommand {
|
|||
#[arg(long, requires = "fix")]
|
||||
/// Fix code even if the working directory has staged changes
|
||||
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
|
||||
Fmt {
|
||||
|
|
@ -206,6 +215,9 @@ enum DevCommand {
|
|||
/// cargo dev lint file.rs -- -W clippy::pedantic {n}
|
||||
/// cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||
Lint {
|
||||
/// The Rust edition to use
|
||||
#[arg(long, default_value = "2024")]
|
||||
edition: String,
|
||||
/// The path to a file or package directory to lint
|
||||
path: String,
|
||||
/// Pass extra arguments to cargo/clippy-driver
|
||||
|
|
@ -264,14 +276,25 @@ enum SetupSubcommand {
|
|||
force_override: bool,
|
||||
},
|
||||
/// 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 {
|
||||
#[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)]
|
||||
/// Override an existing toolchain
|
||||
force: bool,
|
||||
#[arg(long, short)]
|
||||
/// Point to --release clippy binary
|
||||
release: bool,
|
||||
#[arg(long, default_value = "clippy")]
|
||||
#[arg(long, short, default_value = "clippy")]
|
||||
/// Name of the toolchain
|
||||
name: String,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::utils::{clippy_project_root, clippy_version};
|
||||
use indoc::{formatdoc, writedoc};
|
||||
use std::fmt;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fmt, io};
|
||||
|
||||
struct LintData<'a> {
|
||||
pass: &'a str,
|
||||
|
|
@ -25,7 +24,7 @@ impl<T> Context for io::Result<T> {
|
|||
Ok(t) => Ok(t),
|
||||
Err(e) => {
|
||||
let message = format!("{}: {e}", text.as_ref());
|
||||
Err(io::Error::new(ErrorKind::Other, message))
|
||||
Err(io::Error::other(message))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ use std::env::current_dir;
|
|||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::utils::exit_if_err;
|
||||
|
||||
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() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -48,14 +51,22 @@ pub fn create(force: bool, release: bool, name: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
symlink_bin("cargo-clippy", &dest, release);
|
||||
symlink_bin("clippy-driver", &dest, release);
|
||||
let status = Command::new("cargo")
|
||||
.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!("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)]
|
||||
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();
|
||||
dest.extend(["bin", &file_name]);
|
||||
|
||||
symlink(src, dest).unwrap();
|
||||
if standalone {
|
||||
fs::copy(src, dest).unwrap();
|
||||
} else {
|
||||
symlink(src, dest).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin autogenerated version
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
# end autogenerated version
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ impl ApproxConstant {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
|
||||
impl LateLintPass<'_> for ApproxConstant {
|
||||
fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) {
|
||||
match lit.node {
|
||||
LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,24 @@ declare_clippy_lint! {
|
|||
/// Checks for usage of `as` conversions.
|
||||
///
|
||||
/// Note that this lint is specialized in linting *every single* use of `as`
|
||||
/// regardless of whether good alternatives exist or not.
|
||||
/// If you want more 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`.
|
||||
/// 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).
|
||||
/// regardless of whether good alternatives exist or not. If you want more
|
||||
/// precise lints for `as`, please consider using these separate lints:
|
||||
///
|
||||
/// - clippy::cast_lossless
|
||||
/// - clippy::cast_possible_truncation
|
||||
/// - 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?
|
||||
/// `as` conversions will perform many kinds of
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
use super::MIXED_ATTRIBUTES_STYLE;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute};
|
||||
|
|
@ -6,6 +5,7 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_lint::{EarlyContext, LintContext};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{SourceFile, Span, Symbol};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
enum SimpleAttrKind {
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ declare_clippy_lint! {
|
|||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.84.0"]
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
|
||||
use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -16,18 +16,27 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
|
|||
|
||||
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
|
||||
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)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
item_span,
|
||||
"item uses `packed` representation without ABI-qualification",
|
||||
|diag| {
|
||||
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed_span, "`packed` representation set here");
|
||||
diag.warn(
|
||||
"unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI",
|
||||
)
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed_span, "`packed` representation set here");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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::{match_def_path, paths};
|
||||
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]);
|
||||
|
||||
pub struct AwaitHolding {
|
||||
def_ids: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>,
|
||||
}
|
||||
|
||||
impl AwaitHolding {
|
||||
|
|
@ -247,25 +247,26 @@ impl AwaitHolding {
|
|||
);
|
||||
},
|
||||
);
|
||||
} else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) {
|
||||
emit_invalid_type(cx, ty_cause.source_info.span, path, reason);
|
||||
} else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) {
|
||||
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(
|
||||
cx,
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
span,
|
||||
format!("holding a disallowed type across an await point `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
disallowed_path.diag_amendment(span),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_attr_parsing::RustcVersion;
|
||||
|
|
@ -353,7 +354,8 @@ impl SuggestContext<'_, '_, '_> {
|
|||
self.output.push_str(&str);
|
||||
} else {
|
||||
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(_) => {
|
||||
|
|
@ -553,7 +555,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
|
|||
_ => simplified.push(Bool::Not(Box::new(simple.clone()))),
|
||||
}
|
||||
let simple_negated = simple_negate(simple);
|
||||
if simplified.iter().any(|s| *s == simple_negated) {
|
||||
if simplified.contains(&simple_negated) {
|
||||
continue;
|
||||
}
|
||||
simplified.push(simple_negated);
|
||||
|
|
|
|||
|
|
@ -142,11 +142,11 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i
|
|||
expr = recv;
|
||||
}
|
||||
|
||||
if METHODS_POW.iter().any(|&name| method_name == name)
|
||||
if METHODS_POW.contains(&method_name)
|
||||
&& let [arg] = args
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,8 +134,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```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"]
|
||||
pub CAST_POSSIBLE_WRAP,
|
||||
pedantic,
|
||||
|
|
@ -747,7 +753,7 @@ declare_clippy_lint! {
|
|||
/// t as *const T as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.81.0"]
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub AS_POINTER_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as *mut _` and `as *const _` conversion"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use rustc_session::impl_lint_pass;
|
|||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -130,11 +129,6 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// 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
|
||||
/// ```ignore
|
||||
/// 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 replace_span = first_line_span.with_hi(span.hi());
|
||||
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 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 = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
|
||||
let suggestion = reindent_multiline(&suggestion, true, cond_indent);
|
||||
(replace_span, suggestion.to_string())
|
||||
});
|
||||
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 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());
|
||||
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::disallowed_types::DISALLOWED_TYPES_INFO,
|
||||
crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
|
||||
crate::doc::DOC_LAZY_CONTINUATION_INFO,
|
||||
crate::doc::DOC_LINK_CODE_INFO,
|
||||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||
crate::doc::DOC_MARKDOWN_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::TO_STRING_IN_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_impl::PRINT_IN_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_UNTYPED_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::NEEDLESS_LIFETIMES_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_IN_OR_PATTERNS_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_UNINIT_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::INSPECT_FOR_EACH_INFO,
|
||||
crate::methods::INTO_ITER_ON_REF_INFO,
|
||||
crate::methods::IO_OTHER_ERROR_INFO,
|
||||
crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
|
||||
crate::methods::ITERATOR_STEP_BY_ZERO_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_WITH_DRAIN_INFO,
|
||||
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
||||
crate::methods::MANUAL_CONTAINS_INFO,
|
||||
crate::methods::MANUAL_C_STR_LITERALS_INFO,
|
||||
crate::methods::MANUAL_FILTER_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_DEREF_INFO,
|
||||
crate::methods::OPTION_FILTER_MAP_INFO,
|
||||
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
|
||||
crate::methods::OPTION_MAP_OR_NONE_INFO,
|
||||
crate::methods::OR_FUN_CALL_INFO,
|
||||
crate::methods::OR_THEN_UNWRAP_INFO,
|
||||
|
|
@ -483,6 +488,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::SUSPICIOUS_SPLITN_INFO,
|
||||
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
|
||||
crate::methods::TYPE_ID_ON_BOX_INFO,
|
||||
crate::methods::UNBUFFERED_BYTES_INFO,
|
||||
crate::methods::UNINIT_ASSUMED_INIT_INFO,
|
||||
crate::methods::UNIT_HASH_INFO,
|
||||
crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
|
||||
|
|
@ -684,6 +690,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::single_call_fn::SINGLE_CALL_FN_INFO,
|
||||
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_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::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
|
||||
crate::size_of_ref::SIZE_OF_REF_INFO,
|
||||
|
|
@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::types::BOX_COLLECTION_INFO,
|
||||
crate::types::LINKEDLIST_INFO,
|
||||
crate::types::OPTION_OPTION_INFO,
|
||||
crate::types::OWNED_COW_INFO,
|
||||
crate::types::RC_BUFFER_INFO,
|
||||
crate::types::RC_MUTEX_INFO,
|
||||
crate::types::REDUNDANT_ALLOCATION_INFO,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
|
|
|
|||
|
|
@ -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::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::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`
|
||||
]}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
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::macros::macro_backtrace;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{
|
||||
AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
|
||||
|
|
@ -60,7 +58,7 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct DisallowedMacros {
|
||||
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
seen: FxHashSet<ExpnId>,
|
||||
// Track the most recently seen node that can have a `derive` attribute.
|
||||
// Needed to use the correct lint level.
|
||||
|
|
@ -91,13 +89,9 @@ impl DisallowedMacros {
|
|||
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 add_note = |diag: &mut Diag<'_, _>| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
};
|
||||
let add_note = disallowed_path.diag_amendment(mac.span);
|
||||
if matches!(mac.kind, MacroKind::Derive)
|
||||
&& let Some(derive_src) = derive_src
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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 rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
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
|
||||
/// # is disallowed.
|
||||
/// { 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 {
|
||||
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
|
||||
impl DisallowedMethods {
|
||||
|
|
@ -83,17 +85,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
|||
},
|
||||
_ => return,
|
||||
};
|
||||
if let Some(&(path, reason)) = self.disallowed.get(&id) {
|
||||
if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DISALLOWED_METHODS,
|
||||
span,
|
||||
format!("use of a disallowed method `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
disallowed_path.diag_amendment(span),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
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
|
||||
/// # is disallowed.
|
||||
/// { 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 {
|
||||
def_ids: DefIdMap<(&'static str, Option<&'static str>)>,
|
||||
prim_tys: FxHashMap<PrimTy, (&'static str, Option<&'static str>)>,
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
|
||||
impl DisallowedTypes {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
let mut def_ids = DefIdMap::default();
|
||||
let mut prim_tys = FxHashMap::default();
|
||||
for x in &conf.disallowed_types {
|
||||
let path: Vec<_> = x.path().split("::").collect::<Vec<_>>();
|
||||
let reason = x.reason();
|
||||
for disallowed_path in &conf.disallowed_types {
|
||||
let path: Vec<_> = disallowed_path.path().split("::").collect::<Vec<_>>();
|
||||
for res in clippy_utils::def_path_res(tcx, &path) {
|
||||
match res {
|
||||
Res::Def(_, id) => {
|
||||
def_ids.insert(id, (x.path(), reason));
|
||||
def_ids.insert(id, (disallowed_path.path(), disallowed_path));
|
||||
},
|
||||
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) {
|
||||
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::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x,
|
||||
_ => return,
|
||||
|
|
@ -88,11 +90,7 @@ impl DisallowedTypes {
|
|||
DISALLOWED_TYPES,
|
||||
span,
|
||||
format!("use of a disallowed type `{path}`"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
disallowed_path.diag_amendment(span),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle};
|
||||
|
||||
use super::DOC_INCLUDE_WITHOUT_CFG;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ fn map_container_to_text(c: &super::Container) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Adjust the parameters as necessary
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
doc: &str,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,28 @@ declare_clippy_lint! {
|
|||
"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! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
|
|
@ -453,7 +475,7 @@ declare_clippy_lint! {
|
|||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"ensure list items are not overindented"
|
||||
|
|
@ -513,7 +535,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
|
||||
/// ```
|
||||
#[clippy::version = "1.84.0"]
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_INCLUDE_WITHOUT_CFG,
|
||||
restriction,
|
||||
"check if files included in documentation are behind `cfg(doc)`"
|
||||
|
|
@ -539,7 +561,7 @@ declare_clippy_lint! {
|
|||
/// //!
|
||||
/// //! [link]: destination (for link reference definition)
|
||||
/// ```
|
||||
#[clippy::version = "1.84.0"]
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_NESTED_REFDEFS,
|
||||
suspicious,
|
||||
"link reference defined in list item or quote"
|
||||
|
|
@ -560,6 +582,7 @@ impl Documentation {
|
|||
}
|
||||
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_LINK_CODE,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
DOC_NESTED_REFDEFS,
|
||||
|
|
@ -577,7 +600,6 @@ impl_lint_pass!(Documentation => [
|
|||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
]);
|
||||
|
||||
|
||||
impl EarlyLintPass for Documentation {
|
||||
fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
|
||||
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(
|
||||
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
|
||||
} else {
|
||||
Some((attr, None))
|
||||
|
|
@ -747,6 +769,21 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
|
||||
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
|
||||
let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;
|
||||
let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb));
|
||||
|
|
@ -770,6 +807,66 @@ enum Container {
|
|||
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.
|
||||
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
|
||||
/// 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...
|
||||
start -= 1;
|
||||
}
|
||||
start - range.start
|
||||
|
||||
if start > range.start {
|
||||
start - range.start
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0
|
||||
|
|
@ -1092,6 +1194,10 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
|
|||
|
||||
#[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type
|
||||
fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {
|
||||
if range.end < range.start {
|
||||
return None;
|
||||
}
|
||||
|
||||
let offset = range.start;
|
||||
let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();
|
||||
let mut start = None;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::Range;
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::AttrStyle;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -37,7 +37,12 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
|
|||
attrs
|
||||
.iter()
|
||||
.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 sugg = match kind {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Attribute, Item, ItemKind};
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
|
|
@ -44,7 +44,7 @@ pub(super) fn check(
|
|||
let mut should_suggest_empty_doc = false;
|
||||
|
||||
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);
|
||||
let doc = comment.as_str();
|
||||
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
|
||||
// because it might just need to add an empty line 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();
|
||||
if len >= first_paragraph_len {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -289,10 +289,13 @@ impl EmptyLineAfter {
|
|||
format!("empty {lines} after {kind_desc}"),
|
||||
|diag| {
|
||||
let info = self.items.last().unwrap();
|
||||
diag.span_label(info.span, match kind {
|
||||
StopKind::Attr => format!("the attribute applies to this {}", info.kind),
|
||||
StopKind::Doc(_) => format!("the comment documents this {}", info.kind),
|
||||
});
|
||||
diag.span_label(
|
||||
info.span,
|
||||
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(
|
||||
format!("if the empty {lines} {are} unintentional, remove {them}"),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
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::visitors::for_each_expr;
|
||||
use clippy_utils::{
|
||||
SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
|
||||
peel_hir_expr_while,
|
||||
|
|
@ -7,11 +8,12 @@ use clippy_utils::{
|
|||
use core::fmt::{self, Write};
|
||||
use rustc_errors::Applicability;
|
||||
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_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -135,8 +137,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
format!(
|
||||
"match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
|
||||
{indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
|
||||
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
|
||||
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
|
||||
reindent_multiline(&then_str, true, Some(4 + indent_str.len())),
|
||||
reindent_multiline(&else_str, true, Some(4 + indent_str.len())),
|
||||
entry = map_ty.entry_path(),
|
||||
)
|
||||
}
|
||||
|
|
@ -329,7 +331,7 @@ impl<'tcx> Edit<'tcx> {
|
|||
if let Self::Insertion(i) = self { Some(i) } else { None }
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Insertion<'tcx> {
|
||||
call: &'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.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;
|
||||
},
|
||||
_ => match expr.kind {
|
||||
|
|
@ -542,6 +544,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
|||
ExprKind::InlineAsm(_) => {
|
||||
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 &=
|
||||
|
|
@ -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> {
|
||||
edits: Vec<Edit<'tcx>>,
|
||||
allow_insert_closure: bool,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_config::Conf;
|
||||
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_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -10,7 +11,6 @@ use rustc_session::impl_lint_pass;
|
|||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
pub struct BoxedLocal {
|
||||
too_large_for_stack: u64,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use clippy_utils::usage::{local_used_after_expr, local_used_in};
|
|||
use clippy_utils::{
|
||||
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_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
|
@ -15,7 +16,6 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
use arrayvec::ArrayVec;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{
|
||||
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,
|
||||
root_macro_call_first_node,
|
||||
};
|
||||
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::{is_diag_trait_item, is_from_proc_macro};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::{
|
||||
FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
|
||||
FormatPlaceholder, FormatTrait,
|
||||
};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::{List, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
|
@ -50,6 +51,36 @@ declare_clippy_lint! {
|
|||
"`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! {
|
||||
/// ### What it does
|
||||
/// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
|
||||
|
|
@ -162,31 +193,35 @@ declare_clippy_lint! {
|
|||
"use of a format specifier that has no effect"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatArgs => [
|
||||
impl_lint_pass!(FormatArgs<'_> => [
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
UNINLINED_FORMAT_ARGS,
|
||||
UNNECESSARY_DEBUG_FORMATTING,
|
||||
UNUSED_FORMAT_SPECS,
|
||||
]);
|
||||
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct FormatArgs {
|
||||
pub struct FormatArgs<'tcx> {
|
||||
format_args: FormatArgsStorage,
|
||||
msrv: Msrv,
|
||||
ignore_mixed: bool,
|
||||
ty_feature_map: FxHashMap<Ty<'tcx>, Option<Symbol>>,
|
||||
}
|
||||
|
||||
impl FormatArgs {
|
||||
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
|
||||
impl<'tcx> FormatArgs<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
|
||||
let ty_feature_map = make_ty_feature_map(tcx);
|
||||
Self {
|
||||
format_args,
|
||||
msrv: conf.msrv.clone(),
|
||||
ignore_mixed: conf.allow_mixed_uninlined_format_args,
|
||||
ty_feature_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>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& is_format_macro(cx, macro_call.def_id)
|
||||
|
|
@ -198,6 +233,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||
macro_call: ¯o_call,
|
||||
format_args,
|
||||
ignore_mixed: self.ignore_mixed,
|
||||
ty_feature_map: &self.ty_feature_map,
|
||||
};
|
||||
|
||||
linter.check_templates();
|
||||
|
|
@ -217,9 +253,10 @@ struct FormatArgsExpr<'a, 'tcx> {
|
|||
macro_call: &'a MacroCall,
|
||||
format_args: &'a rustc_ast::FormatArgs,
|
||||
ignore_mixed: bool,
|
||||
ty_feature_map: &'a FxHashMap<Ty<'tcx>, Option<Symbol>>,
|
||||
}
|
||||
|
||||
impl FormatArgsExpr<'_, '_> {
|
||||
impl<'tcx> FormatArgsExpr<'_, 'tcx> {
|
||||
fn check_templates(&self) {
|
||||
for piece in &self.format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||
|
|
@ -237,6 +274,11 @@ impl FormatArgsExpr<'_, '_> {
|
|||
self.check_format_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 +481,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)> {
|
||||
self.format_args.template.iter().flat_map(|piece| match piece {
|
||||
FormatArgsPiece::Placeholder(placeholder) => {
|
||||
|
|
@ -465,6 +534,41 @@ impl FormatArgsExpr<'_, '_> {
|
|||
.at_most_one()
|
||||
.is_err()
|
||||
}
|
||||
|
||||
fn can_display_format(&self, ty: Ty<'tcx>) -> bool {
|
||||
let ty = ty.peel_refs();
|
||||
|
||||
if let Some(feature) = self.ty_feature_map.get(&ty)
|
||||
&& feature.is_none_or(|feature| self.cx.tcx.features().enabled(feature))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Even if `ty` is not in `self.ty_feature_map`, check whether `ty` implements `Deref` with
|
||||
// a `Target` that is in `self.ty_feature_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(feature) = self.ty_feature_map.get(&target_ty)
|
||||
&& feature.is_none_or(|feature| self.cx.tcx.features().enabled(feature))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn make_ty_feature_map(tcx: TyCtxt<'_>) -> FxHashMap<Ty<'_>, Option<Symbol>> {
|
||||
[(sym::OsStr, Some(Symbol::intern("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>)
|
||||
|
|
|
|||
|
|
@ -118,18 +118,23 @@ fn check_needless_must_use(
|
|||
fn_header_span,
|
||||
"this unit-returning function has a `#[must_use]` attribute",
|
||||
|diag| {
|
||||
diag.span_suggestion(attr.span(), "remove the attribute", "", Applicability::MachineApplicable);
|
||||
diag.span_suggestion(
|
||||
attr.span(),
|
||||
"remove the attribute",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
|
||||
// issue #12320.
|
||||
// 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
|
||||
// re-added.
|
||||
// honestly looked incorrect, and is a little hard to support for a little bit now. Some day this
|
||||
// could be re-added.
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
DOUBLE_MUST_USE,
|
||||
MUST_USE_UNIT,
|
||||
fn_header_span,
|
||||
"this unit-returning function has a `#[must_use]` attribute",
|
||||
Some(attr.span()),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_abi::ExternAbi;
|
||||
use rustc_hir::{self as hir, intravisit};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_trait_impl_item;
|
||||
|
|
@ -23,11 +23,19 @@ pub(super) fn check_fn(
|
|||
intravisit::FnKind::Method(
|
||||
_,
|
||||
&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,
|
||||
decl,
|
||||
span.with_hi(decl.output.span().hi()),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -107,7 +106,7 @@ fn make_sugg<'a>(
|
|||
els_span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
) -> Cow<'a, str> {
|
||||
) -> String {
|
||||
let cond_inner_snip = snippet(sess, cond_inner, default);
|
||||
let els_snip = snippet(sess, els_span, default);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
|
|
@ -130,5 +129,5 @@ fn make_sugg<'a>(
|
|||
_ => String::new(),
|
||||
};
|
||||
|
||||
reindent_multiline(suggestion.into(), true, indent)
|
||||
reindent_multiline(&suggestion, true, indent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
expr.span,
|
||||
format!("this could be simplified with `bool::{method_name}`"),
|
||||
|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)
|
||||
.maybe_par()
|
||||
.to_string();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
use clippy_utils::{return_ty, trait_ref_of_method};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ declare_clippy_lint! {
|
|||
/// will mistakenly imply that it is possible for `x` to be outside the range of
|
||||
/// `u8`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// https://github.com/rust-lang/rust-clippy/issues/886
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: u8 = 1;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ use clippy_config::Conf;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_ast::{Attribute, AttrArgs, AttrKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
|
|
|||
|
|
@ -340,6 +340,7 @@ mod significant_drop_tightening;
|
|||
mod single_call_fn;
|
||||
mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod single_option_map;
|
||||
mod single_range_in_vec_init;
|
||||
mod size_of_in_element_count;
|
||||
mod size_of_ref;
|
||||
|
|
@ -407,9 +408,9 @@ mod zombie_processes;
|
|||
|
||||
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
|
||||
use clippy_utils::macros::FormatArgsStorage;
|
||||
use utils::attr_collector::{AttrCollector, AttrStorage};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{Lint, LintId};
|
||||
use utils::attr_collector::{AttrCollector, AttrStorage};
|
||||
|
||||
/// Register all pre expansion lints
|
||||
///
|
||||
|
|
@ -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(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf)));
|
||||
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_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
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(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
|
||||
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(manual_slice_size_calculation::ManualSliceSizeCalculation));
|
||||
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::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(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(ineffective_open_options::IneffectiveOpenOptions));
|
||||
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(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(single_option_map::SingleOptionMap));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ declare_clippy_lint! {
|
|||
/// them leads to more readable code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - We bail out if the function has a `where` clause where lifetimes
|
||||
/// are mentioned due to potential false positives.
|
||||
/// This lint ignores functions with `where` clauses that reference
|
||||
/// lifetimes to prevent false positives.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
|
|
@ -62,6 +62,38 @@ declare_clippy_lint! {
|
|||
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! {
|
||||
/// ### What it does
|
||||
/// Checks for lifetimes in generics that are never used
|
||||
|
|
@ -104,7 +136,11 @@ impl Lifetimes {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
|
|
@ -746,6 +782,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
|
|||
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.
|
||||
fn report_elidable_lifetimes(
|
||||
cx: &LateContext<'_>,
|
||||
|
|
@ -763,9 +808,29 @@ fn report_elidable_lifetimes(
|
|||
.collect::<Vec<_>>()
|
||||
.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(
|
||||
cx,
|
||||
NEEDLESS_LIFETIMES,
|
||||
lint,
|
||||
elidable_lts
|
||||
.iter()
|
||||
.map(|<| cx.tcx.def_span(lt))
|
||||
|
|
@ -785,7 +850,7 @@ fn report_elidable_lifetimes(
|
|||
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);
|
||||
}
|
||||
},
|
||||
|
|
@ -796,7 +861,7 @@ fn elision_suggestions(
|
|||
cx: &LateContext<'_>,
|
||||
generics: &Generics<'_>,
|
||||
elidable_lts: &[LocalDefId],
|
||||
usages: &[Lifetime],
|
||||
usages: &[ElidableUsage],
|
||||
) -> Option<Vec<(Span, String)>> {
|
||||
let explicit_params = generics
|
||||
.params
|
||||
|
|
@ -836,26 +901,21 @@ fn elision_suggestions(
|
|||
.collect::<Option<Vec<_>>>()?
|
||||
};
|
||||
|
||||
suggestions.extend(
|
||||
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(..), ..
|
||||
}) => {
|
||||
// expand `&'a T` to `&'a T`
|
||||
// ^^ ^^^
|
||||
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
|
||||
suggestions.extend(usages.iter().map(|&usage| {
|
||||
match usage {
|
||||
ElidableUsage::Ref(span) => {
|
||||
// expand `&'a T` to `&'a T`
|
||||
// ^^ ^^^
|
||||
let span = cx.sess().source_map().span_extend_while_whitespace(span);
|
||||
|
||||
(span, String::new())
|
||||
},
|
||||
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
|
||||
_ => (usage.ident.span, String::from("'_")),
|
||||
}
|
||||
}),
|
||||
);
|
||||
(span, String::new())
|
||||
},
|
||||
ElidableUsage::Other(span) => {
|
||||
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
|
||||
(span, String::from("'_"))
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
Some(suggestions)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,26 @@
|
|||
use clippy_config::Conf;
|
||||
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::{is_diag_item_method, is_trait_method, path_to_local_id};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Closure, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub struct LinesFilterMapOk {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl LinesFilterMapOk {
|
||||
pub fn new(conf: &Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
|
||||
|
|
@ -55,11 +69,13 @@ declare_clippy_lint! {
|
|||
suspicious,
|
||||
"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 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind
|
||||
if self.msrv.meets(msrvs::MAP_WHILE)
|
||||
&& let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
&& let fm_method_str = fm_method.ident.as_str()
|
||||
&& matches!(fm_method_str, "filter_map" | "flat_map" | "flatten")
|
||||
|
|
@ -85,6 +101,8 @@ impl LateLintPass<'_> for LinesFilterMapOk {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> bool {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_session::declare_lint_pass;
|
|||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::mir::enclosing_mir;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -79,9 +80,9 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option<Strin
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for LiteralStringWithFormattingArg {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
impl<'tcx> LateLintPass<'tcx> for LiteralStringWithFormattingArg {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if expr.span.from_expansion() || expr.span.is_dummy() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
|
|
@ -95,6 +96,9 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
|
|||
},
|
||||
_ => return,
|
||||
};
|
||||
if is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
let fmt_str = symbol.as_str();
|
||||
let lo = expr.span.lo();
|
||||
let mut current = fmt_str;
|
||||
|
|
@ -124,7 +128,11 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
|
|||
pos.start += 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 start > 1 && fmt_str[..start].ends_with("\\u") {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::MANUAL_FLATTEN;
|
||||
use super::utils::make_iterator_snippet;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -18,6 +19,7 @@ pub(super) fn check<'tcx>(
|
|||
arg: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let inner_expr = peel_blocks_with_stmt(body);
|
||||
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)
|
||||
// Ensure expr in `if let` is not used afterwards
|
||||
&& !is_local_used(cx, if_then, pat_hir_id)
|
||||
&& msrv.meets(msrvs::ITER_FLATTEN)
|
||||
{
|
||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||
// Prepare the error message
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
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::source::{HasSession, snippet_with_applicability};
|
||||
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 rustc_ast::ast::LitKind;
|
||||
use rustc_ast::{RangeLimits, UnOp};
|
||||
|
|
@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let ExprKind::Block(..) = body.kind
|
||||
// Check if the body is an assignment to a slice element.
|
||||
&& 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.
|
||||
&& let ExprKind::MethodCall(path, recv,..) = end.kind
|
||||
&& path.ident.name == sym::len
|
||||
|
|
@ -54,10 +54,13 @@ pub(super) fn check<'tcx>(
|
|||
&& !assignval.span.from_expansion()
|
||||
// It is generally not equivalent to use the `fill` method if `assignval` can have side effects
|
||||
&& 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.
|
||||
&& let Some(clone_trait) = cx.tcx.lang_items().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)
|
||||
{
|
||||
sugg(cx, body, expr, slice.span, assignval.span);
|
||||
}
|
||||
|
|
@ -73,7 +76,8 @@ pub(super) fn check<'tcx>(
|
|||
&& local == pat.hir_id
|
||||
&& !assignval.span.from_expansion()
|
||||
&& 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.
|
||||
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[])
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ impl Loops {
|
|||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
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);
|
||||
unused_enumerate_index::check(cx, pat, arg, body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,21 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[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"]
|
||||
pub MACRO_USE_IMPORTS,
|
||||
|
|
@ -103,11 +117,6 @@ impl LateLintPass<'_> for MacroUseImports {
|
|||
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<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
self.push_unique_macro(cx, expr.span);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
&& let Some(closure_body) = desugared_async_block(cx, block)
|
||||
&& let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
|
||||
cx.tcx.hir_node_by_def_id(fn_def_id)
|
||||
&& !span.from_expansion()
|
||||
{
|
||||
let header_span = span.with_hi(ret_ty.span.hi());
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
|||
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
|
||||
&& let BinOpKind::Mul = &bin_op.node
|
||||
&& !expr.span.from_expansion()
|
||||
&& self.msrv.meets(msrvs::MANUAL_BITS)
|
||||
&& self.msrv.meets(msrvs::INTEGER_BITS)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& left_expr.span.ctxt() == ctxt
|
||||
&& right_expr.span.ctxt() == ctxt
|
||||
|
|
|
|||
|
|
@ -2,19 +2,21 @@ use clippy_config::Conf;
|
|||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
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::{eq_expr_value, higher};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext as _};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::{Symbol, sym};
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -95,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
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() {
|
||||
let kind_word = match strip_kind {
|
||||
StripKind::Prefix => "prefix",
|
||||
|
|
@ -103,10 +105,29 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
};
|
||||
|
||||
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(
|
||||
cx,
|
||||
MANUAL_STRIP,
|
||||
strippings[0],
|
||||
strippings[0].span,
|
||||
format!("stripping a {kind_word} manually"),
|
||||
|diag| {
|
||||
diag.span_note(test_span, format!("the {kind_word} was tested here"));
|
||||
|
|
@ -115,14 +136,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
iter::once((
|
||||
test_span,
|
||||
format!(
|
||||
"if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
|
||||
snippet(cx, target_arg.span, ".."),
|
||||
snippet(cx, pattern.span, "..")
|
||||
"if let Some({ident_name}) = {}.strip_{kind_word}({}) ",
|
||||
snippet_with_applicability(cx, target_arg.span, "_", &mut app),
|
||||
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(),
|
||||
Applicability::HasPlaceholders,
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -188,19 +215,21 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
|
|||
/// 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}`
|
||||
/// method.
|
||||
/// Also, all bindings found during the visit are counted and returned.
|
||||
fn find_stripping<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
strip_kind: StripKind,
|
||||
target: Res,
|
||||
pattern: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) -> Vec<Span> {
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap<Symbol, usize>) {
|
||||
struct StrippingFinder<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
strip_kind: StripKind,
|
||||
target: Res,
|
||||
pattern: &'tcx Expr<'tcx>,
|
||||
results: Vec<Span>,
|
||||
results: Vec<&'tcx Expr<'tcx>>,
|
||||
bindings: FxHashMap<Symbol, usize>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> {
|
||||
|
|
@ -215,7 +244,7 @@ fn find_stripping<'tcx>(
|
|||
match (self.strip_kind, start, end) {
|
||||
(StripKind::Prefix, Some(start), None) => {
|
||||
if eq_pattern_length(self.cx, self.pattern, start) {
|
||||
self.results.push(ex.span);
|
||||
self.results.push(ex);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
|
@ -232,7 +261,7 @@ fn find_stripping<'tcx>(
|
|||
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
|
||||
&& eq_pattern_length(self.cx, self.pattern, right)
|
||||
{
|
||||
self.results.push(ex.span);
|
||||
self.results.push(ex);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
|
@ -242,6 +271,13 @@ fn find_stripping<'tcx>(
|
|||
|
||||
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 {
|
||||
|
|
@ -250,7 +286,8 @@ fn find_stripping<'tcx>(
|
|||
target,
|
||||
pattern,
|
||||
results: vec![],
|
||||
bindings: FxHashMap::default(),
|
||||
};
|
||||
walk_expr(&mut finder, expr);
|
||||
finder.results
|
||||
(finder.results, finder.bindings)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
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_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
|
||||
|
|
@ -132,13 +133,23 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok
|
|||
Applicability::MachineApplicable
|
||||
};
|
||||
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(
|
||||
cx,
|
||||
MANUAL_OK_ERR,
|
||||
expr.span,
|
||||
format!("manual implementation of `{method}`"),
|
||||
"replace with",
|
||||
format!("{scrut}.{method}()"),
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ fn lint<'tcx>(
|
|||
or_body_snippet: &str,
|
||||
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 suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
|
||||
use clippy_utils::{
|
||||
CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res,
|
||||
path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
|
||||
CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor,
|
||||
path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
|
||||
};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -73,7 +73,7 @@ where
|
|||
}
|
||||
|
||||
// `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;
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +124,12 @@ where
|
|||
};
|
||||
|
||||
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 {
|
||||
if !some_expr.needs_unsafe_block
|
||||
&& 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}| unsafe {{ {closure_expr_snip} }}")
|
||||
} else {
|
||||
format!("|{annotation}{some_binding}| {closure_expr_snip}")
|
||||
}
|
||||
format!("|{annotation}{some_binding}| {closure_body}")
|
||||
}
|
||||
} else if !is_wild_none && explicit_ref.is_none() {
|
||||
// TODO: handle explicit reference annotations.
|
||||
let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
|
||||
if some_expr.needs_unsafe_block {
|
||||
format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}")
|
||||
} else {
|
||||
format!("|{pat_snip}| {closure_expr_snip}")
|
||||
}
|
||||
format!("|{pat_snip}| {closure_body}")
|
||||
} else {
|
||||
// Refutable bindings and mixed reference annotations can't be handled by `map`.
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
|||
return false;
|
||||
}
|
||||
|
||||
let result = match ty.kind() {
|
||||
match ty.kind() {
|
||||
rustc_middle::ty::Adt(adt, args) => {
|
||||
// if some field has significant drop,
|
||||
adt.all_fields()
|
||||
|
|
@ -223,9 +223,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
|||
rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)),
|
||||
rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
|||
is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -43,6 +43,31 @@ declare_clippy_lint! {
|
|||
"replacing an `Option` with `None` instead of `take()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `mem::replace()` on an `Option` with `Some(…)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Option` already has the method `replace()` for
|
||||
/// taking its current value (Some(…) or None) and replacing it with
|
||||
/// `Some(…)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut an_option = Some(0);
|
||||
/// let replaced = std::mem::replace(&mut an_option, Some(1));
|
||||
/// ```
|
||||
/// Is better expressed with:
|
||||
/// ```no_run
|
||||
/// let mut an_option = Some(0);
|
||||
/// let taken = an_option.replace(1);
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MEM_REPLACE_OPTION_WITH_SOME,
|
||||
style,
|
||||
"replacing an `Option` with `Some` instead of `replace()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `mem::replace(&mut _, mem::uninitialized())`
|
||||
|
|
@ -101,28 +126,67 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
impl_lint_pass!(MemReplace =>
|
||||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
// argument's type. All that's left is to get
|
||||
// the replacee's expr after peeling off the `&mut`
|
||||
let sugg_expr = peel_ref_operators(cx, dest);
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MEM_REPLACE_OPTION_WITH_NONE,
|
||||
expr_span,
|
||||
"replacing an `Option` with `None`",
|
||||
"consider `Option::take()` instead",
|
||||
format!(
|
||||
"{}.take()",
|
||||
Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool {
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
// argument's type. All that's left is to get
|
||||
// the replacee's expr after peeling off the `&mut`
|
||||
let sugg_expr = peel_ref_operators(cx, dest);
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MEM_REPLACE_OPTION_WITH_NONE,
|
||||
expr_span,
|
||||
"replacing an `Option` with `None`",
|
||||
"consider `Option::take()` instead",
|
||||
format!(
|
||||
"{}.take()",
|
||||
Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_replace_option_with_some(
|
||||
cx: &LateContext<'_>,
|
||||
src: &Expr<'_>,
|
||||
dest: &Expr<'_>,
|
||||
expr_span: Span,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
if msrv.meets(msrvs::OPTION_REPLACE)
|
||||
&& let ExprKind::Call(src_func, [src_arg]) = src.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, src_func), OptionSome)
|
||||
{
|
||||
// We do not have to check for a `const` context here, because `core::mem::replace()` and
|
||||
// `Option::replace()` have been const-stabilized simultaneously in version 1.83.0.
|
||||
let sugg_expr = peel_ref_operators(cx, dest);
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MEM_REPLACE_OPTION_WITH_SOME,
|
||||
expr_span,
|
||||
"replacing an `Option` with `Some(..)`",
|
||||
"consider `Option::replace()` instead",
|
||||
format!(
|
||||
"{}.replace({})",
|
||||
Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_par(),
|
||||
snippet_with_applicability(cx, src_arg.span, "_", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
|
|
@ -181,27 +245,34 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
}
|
||||
}
|
||||
|
||||
fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
// disable lint for primitives
|
||||
let expr_type = cx.typeck_results().expr_ty_adjusted(src);
|
||||
if is_non_aggregate_primitive_type(expr_type) {
|
||||
return;
|
||||
}
|
||||
if is_default_equivalent(cx, src) && !expr_span.in_external_macro(cx.tcx.sess.source_map()) {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
fn check_replace_with_default(
|
||||
cx: &LateContext<'_>,
|
||||
src: &Expr<'_>,
|
||||
dest: &Expr<'_>,
|
||||
expr: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
if msrv.meets(msrvs::MEM_TAKE) && is_expr_used_or_unified(cx.tcx, expr)
|
||||
// disable lint for primitives
|
||||
&& let expr_type = cx.typeck_results().expr_ty_adjusted(src)
|
||||
&& !is_non_aggregate_primitive_type(expr_type)
|
||||
&& is_default_equivalent(cx, src)
|
||||
&& !expr.span.in_external_macro(cx.tcx.sess.source_map())
|
||||
&& let Some(top_crate) = std_or_core(cx)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MEM_REPLACE_WITH_DEFAULT,
|
||||
expr_span,
|
||||
expr.span,
|
||||
format!(
|
||||
"replacing a value of type `T` with `T::default()` is better expressed using `{top_crate}::mem::take`"
|
||||
),
|
||||
|diag| {
|
||||
if !expr_span.from_expansion() {
|
||||
if !expr.span.from_expansion() {
|
||||
let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, ""));
|
||||
|
||||
diag.span_suggestion(
|
||||
expr_span,
|
||||
expr.span,
|
||||
"consider using",
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
|
|
@ -209,6 +280,9 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
|||
}
|
||||
},
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,12 +307,12 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
|||
&& cx.tcx.is_diagnostic_item(sym::mem_replace, def_id)
|
||||
{
|
||||
// Check that second argument is `Option::None`
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
check_replace_option_with_none(cx, dest, expr.span);
|
||||
} else if self.msrv.meets(msrvs::MEM_TAKE) && is_expr_used_or_unified(cx.tcx, expr) {
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
if !check_replace_option_with_none(cx, src, dest, expr.span)
|
||||
&& !check_replace_option_with_some(cx, src, dest, expr.span, &self.msrv)
|
||||
&& !check_replace_with_default(cx, src, dest, expr, &self.msrv)
|
||||
{
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
}
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
|
|
|
|||
|
|
@ -57,14 +57,13 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
|
||||
let suggestion_source = reindent_multiline(
|
||||
format!(
|
||||
&format!(
|
||||
"std::path::Path::new({})
|
||||
.extension()
|
||||
.map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))",
|
||||
recv_source,
|
||||
ext_str.strip_prefix('.').unwrap()
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
true,
|
||||
Some(indent_of(cx, call_span).unwrap_or(0) + 4),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_mutable, is_trait_method, path_to_local};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::{Expr, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_span::{Span, sym};
|
||||
|
|
@ -28,14 +28,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
|
|||
// if the resolved method is the same as the provided definition
|
||||
&& fn_def.def_id() == last_def.def_id
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
let mut sugg = vec![(call_span, String::from("next_back()"))];
|
||||
let mut dont_apply = false;
|
||||
// if `self_expr` is a reference, it is mutable because it is used for `.last()`
|
||||
if !(is_mutable(cx, self_expr) || self_type.is_ref()) {
|
||||
if let Some(hir_id) = path_to_local(self_expr)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
{
|
||||
sugg.push((ident.span.shrink_to_lo(), String::from("mut ")));
|
||||
} else {
|
||||
// If we can't make the binding mutable, make the suggestion `Unspecified` to prevent it from being
|
||||
// automatically applied, and add a complementary help message.
|
||||
dont_apply = true;
|
||||
}
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
call_span,
|
||||
expr.span,
|
||||
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
|
||||
"try",
|
||||
"next_back()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let droppable_elements = expr_ty.has_significant_drop(cx.tcx, cx.typing_env());
|
||||
diag.multipart_suggestion(
|
||||
"try",
|
||||
sugg,
|
||||
if dont_apply {
|
||||
Applicability::Unspecified
|
||||
} else if droppable_elements {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
);
|
||||
if droppable_elements {
|
||||
diag.note("this change will alter drop order which may be undesirable");
|
||||
}
|
||||
if dont_apply {
|
||||
diag.span_note(self_expr.span, "this must be made mutable to use `.next_back()`");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, sym};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP};
|
||||
|
||||
|
|
@ -302,7 +301,7 @@ pub(super) fn check(
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `Some` followed by `unwrap`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, map_span)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
|
|
@ -316,7 +315,7 @@ pub(super) fn check(
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `Ok` followed by `unwrap`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, map_span)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
|
|
|
|||
37
clippy_lints/src/methods/io_other_error.rs
Normal file
37
clippy_lints/src/methods/io_other_error.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{IO_ERROR_OTHER, Msrv};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
|
||||
if let [error_kind, error] = args
|
||||
&& !expr.span.from_expansion()
|
||||
&& !error_kind.span.from_expansion()
|
||||
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
|
||||
&& clippy_utils::is_expr_path_def_path(
|
||||
cx,
|
||||
clippy_utils::expr_or_init(cx, error_kind),
|
||||
&clippy_utils::paths::IO_ERRORKIND_OTHER,
|
||||
)
|
||||
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
|
||||
&& msrv.meets(IO_ERROR_OTHER)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
super::IO_OTHER_ERROR,
|
||||
expr.span,
|
||||
"this can be `std::io::Error::other(_)`",
|
||||
|diag| {
|
||||
diag.multipart_suggestion_verbose(
|
||||
"use `std::io::Error::other`",
|
||||
vec![
|
||||
(new_segment.ident.span, "other".to_owned()),
|
||||
(error_kind.span.until(error.span), String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ use rustc_hir as hir;
|
|||
use rustc_hir::QPath;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, sym};
|
||||
use std::borrow::Cow;
|
||||
|
||||
///
|
||||
/// Returns true if the expression is a method call to `method_name`
|
||||
|
|
@ -181,7 +180,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `is_ok` on iterator over `Result`s",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, filter_span)),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
Some(FilterType::IsSome) => span_lint_and_sugg(
|
||||
|
|
@ -190,7 +189,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `is_some` on iterator over `Option`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, filter_span)),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
}
|
||||
|
|
|
|||
113
clippy_lints/src/methods/manual_contains.rs
Normal file
113
clippy_lints/src/methods/manual_contains.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::peel_hir_pat_refs;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::UnOp;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::MANUAL_CONTAINS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
if !expr.span.from_expansion()
|
||||
// check if `iter().any()` can be replaced with `contains()`
|
||||
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||
&& let Body{params: [param],value} = cx.tcx.hir_body(closure.body)
|
||||
&& let ExprKind::Binary(op, lhs, rhs) = value.kind
|
||||
&& let (peeled_ref_pat, _) = peel_hir_pat_refs(param.pat)
|
||||
&& let Some((snip,snip_expr)) = can_replace_with_contains(cx, op, lhs, rhs, peeled_ref_pat.hir_id, &mut app)
|
||||
&& let ref_type = cx.typeck_results().expr_ty_adjusted(recv)
|
||||
&& let ty::Ref(_, inner_type, _) = ref_type.kind()
|
||||
&& let ty::Slice(slice_type) = inner_type.kind()
|
||||
&& *slice_type == cx.typeck_results().expr_ty(snip_expr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_CONTAINS,
|
||||
expr.span,
|
||||
"using `contains()` instead of `iter().any()` is more efficient",
|
||||
"try",
|
||||
format!(
|
||||
"{}.contains({})",
|
||||
snippet_with_applicability(cx, recv.span, "_", &mut app),
|
||||
snip
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum EligibleArg {
|
||||
IsClosureArg,
|
||||
ContainsArg(String),
|
||||
}
|
||||
|
||||
fn try_get_eligible_arg<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
closure_arg_id: HirId,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<(EligibleArg, &'tcx Expr<'tcx>)> {
|
||||
let mut get_snippet = |expr: &Expr<'_>, needs_borrow: bool| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", applicability);
|
||||
EligibleArg::ContainsArg((if needs_borrow { sugg.addr() } else { sugg }).to_string())
|
||||
};
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => {
|
||||
if path.res == Res::Local(closure_arg_id) {
|
||||
Some((EligibleArg::IsClosureArg, expr))
|
||||
} else {
|
||||
Some((get_snippet(expr, true), expr))
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Deref, inner) => {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = inner.kind {
|
||||
if path.res == Res::Local(closure_arg_id) {
|
||||
Some((EligibleArg::IsClosureArg, expr))
|
||||
} else {
|
||||
Some((get_snippet(inner, false), expr))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if switch_to_eager_eval(cx, expr) {
|
||||
Some((get_snippet(expr, true), expr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn can_replace_with_contains<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
bin_op: Spanned<BinOpKind>,
|
||||
left_expr: &'tcx Expr<'tcx>,
|
||||
right_expr: &'tcx Expr<'tcx>,
|
||||
closure_arg_id: HirId,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<(String, &'tcx Expr<'tcx>)> {
|
||||
if bin_op.node != BinOpKind::Eq {
|
||||
return None;
|
||||
}
|
||||
|
||||
let left_candidate = try_get_eligible_arg(cx, left_expr, closure_arg_id, applicability)?;
|
||||
let right_candidate = try_get_eligible_arg(cx, right_expr, closure_arg_id, applicability)?;
|
||||
match (left_candidate, right_candidate) {
|
||||
((EligibleArg::IsClosureArg, _), (EligibleArg::ContainsArg(snip), candidate_expr))
|
||||
| ((EligibleArg::ContainsArg(snip), candidate_expr), (EligibleArg::IsClosureArg, _)) => {
|
||||
Some((snip, candidate_expr))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
|
||||
&& let Some(indent) = indent_of(cx, expr.span)
|
||||
{
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ mod implicit_clone;
|
|||
mod inefficient_to_string;
|
||||
mod inspect_for_each;
|
||||
mod into_iter_on_ref;
|
||||
mod io_other_error;
|
||||
mod is_digit_ascii_radix;
|
||||
mod is_empty;
|
||||
mod iter_cloned_collect;
|
||||
|
|
@ -54,6 +55,7 @@ mod iter_with_drain;
|
|||
mod iterator_step_by_zero;
|
||||
mod join_absolute_paths;
|
||||
mod manual_c_str_literals;
|
||||
mod manual_contains;
|
||||
mod manual_inspect;
|
||||
mod manual_is_variant_and;
|
||||
mod manual_next_back;
|
||||
|
|
@ -82,7 +84,6 @@ mod ok_expect;
|
|||
mod open_options;
|
||||
mod option_as_ref_cloned;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_err_ok;
|
||||
mod option_map_or_none;
|
||||
mod option_map_unwrap_or;
|
||||
mod or_fun_call;
|
||||
|
|
@ -114,6 +115,7 @@ mod suspicious_map;
|
|||
mod suspicious_splitn;
|
||||
mod suspicious_to_owned;
|
||||
mod type_id_on_box;
|
||||
mod unbuffered_bytes;
|
||||
mod uninit_assumed_init;
|
||||
mod unit_hash;
|
||||
mod unnecessary_fallible_conversions;
|
||||
|
|
@ -2642,7 +2644,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_OK_OR,
|
||||
pedantic,
|
||||
style,
|
||||
"finds patterns that can be encoded more concisely with `Option::ok_or`"
|
||||
}
|
||||
|
||||
|
|
@ -3784,31 +3786,6 @@ declare_clippy_lint! {
|
|||
"calls to `Path::join` which will overwrite the original path"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `_.map_or(Err(_), Ok)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely as
|
||||
/// `_.ok_or(_)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let opt = Some(1);
|
||||
/// opt.map_or(Err("error"), Ok);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let opt = Some(1);
|
||||
/// opt.ok_or("error");
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub OPTION_MAP_OR_ERR_OK,
|
||||
style,
|
||||
"using `Option.map_or(Err(_), Ok)`, which is more succinctly expressed as `Option.ok_or(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for iterators of `Result`s using `.filter(Result::is_ok).map(Result::unwrap)` that may
|
||||
|
|
@ -4433,11 +4410,88 @@ declare_clippy_lint! {
|
|||
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `Read::bytes` on types which don't implement `BufRead`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::io::Read;
|
||||
/// use std::fs::File;
|
||||
/// let file = File::open("./bytes.txt").unwrap();
|
||||
/// file.bytes();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, Read};
|
||||
/// use std::fs::File;
|
||||
/// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap());
|
||||
/// file.bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub UNBUFFERED_BYTES,
|
||||
perf,
|
||||
"calling .bytes() is very inefficient when data is not in memory"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `contains()` is more concise and idiomatic, sometimes more fast.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn foo(values: &[u8]) -> bool {
|
||||
/// values.iter().any(|&v| v == 10)
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(values: &[u8]) -> bool {
|
||||
/// values.contains(&10)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_CONTAINS,
|
||||
perf,
|
||||
"unnecessary `iter().any()` on slices that can be replaced with `contains()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// This lint warns on calling `io::Error::new(..)` with a kind of
|
||||
/// `io::ErrorKind::Other`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Since Rust 1.74, there's the `io::Error::other(_)` shortcut.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// let _ = io::Error::new(io::ErrorKind::Other, "bad".to_string());
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = std::io::Error::other("bad".to_string());
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub IO_OTHER_ERROR,
|
||||
style,
|
||||
"calling `std::io::Error::new(std::io::ErrorKind::Other, _)`"
|
||||
}
|
||||
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
allow_expect_in_consts: bool,
|
||||
allow_unwrap_in_consts: bool,
|
||||
allowed_dotfiles: FxHashSet<&'static str>,
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
|
|
@ -4452,6 +4506,8 @@ impl Methods {
|
|||
msrv: conf.msrv.clone(),
|
||||
allow_expect_in_tests: conf.allow_expect_in_tests,
|
||||
allow_unwrap_in_tests: conf.allow_unwrap_in_tests,
|
||||
allow_expect_in_consts: conf.allow_expect_in_consts,
|
||||
allow_unwrap_in_consts: conf.allow_unwrap_in_consts,
|
||||
allowed_dotfiles,
|
||||
format_args,
|
||||
}
|
||||
|
|
@ -4580,7 +4636,6 @@ impl_lint_pass!(Methods => [
|
|||
WAKER_CLONE_WAKE,
|
||||
UNNECESSARY_FALLIBLE_CONVERSIONS,
|
||||
JOIN_ABSOLUTE_PATHS,
|
||||
OPTION_MAP_OR_ERR_OK,
|
||||
RESULT_FILTER_MAP,
|
||||
ITER_FILTER_IS_SOME,
|
||||
ITER_FILTER_IS_OK,
|
||||
|
|
@ -4603,6 +4658,9 @@ impl_lint_pass!(Methods => [
|
|||
MANUAL_REPEAT_N,
|
||||
SLICED_STRING_AS_BYTES,
|
||||
RETURN_AND_THEN,
|
||||
UNBUFFERED_BYTES,
|
||||
MANUAL_CONTAINS,
|
||||
IO_OTHER_ERROR,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
|
@ -4632,6 +4690,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
unnecessary_fallible_conversions::check_function(cx, expr, func);
|
||||
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
|
||||
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
|
||||
io_other_error::check(cx, expr, func, args, &self.msrv);
|
||||
},
|
||||
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||
let method_span = method_call.ident.span;
|
||||
|
|
@ -4860,6 +4919,9 @@ impl Methods {
|
|||
Some(("map", _, [map_arg], _, map_call_span)) => {
|
||||
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
|
||||
},
|
||||
Some(("iter", iter_recv, ..)) => {
|
||||
manual_contains::check(cx, expr, iter_recv, arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
|
|
@ -4879,6 +4941,7 @@ impl Methods {
|
|||
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
|
||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("bytes", []) => unbuffered_bytes::check(cx, expr, recv),
|
||||
("cloned", []) => {
|
||||
cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv);
|
||||
option_as_ref_cloned::check(cx, recv, span);
|
||||
|
|
@ -4946,6 +5009,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
),
|
||||
|
|
@ -4959,6 +5023,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
);
|
||||
|
|
@ -5147,7 +5212,6 @@ impl Methods {
|
|||
("map_or", [def, map]) => {
|
||||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
manual_ok_or::check(cx, expr, recv, def, map);
|
||||
option_map_or_err_ok::check(cx, expr, recv, def, map);
|
||||
unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv);
|
||||
},
|
||||
("map_or_else", [def, map]) => {
|
||||
|
|
@ -5333,6 +5397,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
|
|
@ -5344,6 +5409,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
|
|
@ -5357,7 +5423,7 @@ impl Methods {
|
|||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
||||
},
|
||||
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method);
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
|
@ -5376,6 +5442,9 @@ impl Methods {
|
|||
match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else");
|
||||
},
|
||||
_ => {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::OBFUSCATED_IF_ELSE;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -15,6 +16,7 @@ pub(super) fn check<'tcx>(
|
|||
then_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
then_method_name: &str,
|
||||
unwrap_method_name: &str,
|
||||
) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
||||
|
||||
|
|
@ -31,16 +33,40 @@ pub(super) fn check<'tcx>(
|
|||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
||||
},
|
||||
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
||||
_ => String::new().into(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// FIXME: Add `unwrap_or_else` symbol
|
||||
let els = match unwrap_method_name {
|
||||
"unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability),
|
||||
"unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => {
|
||||
let body = cx.tcx.hir_body(closure.body);
|
||||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
||||
},
|
||||
"unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => {
|
||||
snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()"
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let sugg = format!(
|
||||
"if {} {{ {} }} else {{ {} }}",
|
||||
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
|
||||
if_then,
|
||||
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
||||
els
|
||||
);
|
||||
|
||||
// To be parsed as an expression, the `if { … } else { … }` as the left operand of a binary operator
|
||||
// requires parentheses.
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Binary(_, left, _) = parent_expr.kind
|
||||
&& left.hir_id == expr.hir_id
|
||||
{
|
||||
format!("({sugg})")
|
||||
} else {
|
||||
sugg
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OBFUSCATED_IF_ELSE,
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_res_lang_ctor, path_res};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::OPTION_MAP_OR_ERR_OK;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
or_expr: &'tcx Expr<'_>,
|
||||
map_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
// We check that it's called on an `Option` type.
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option)
|
||||
// We check that first we pass an `Err`.
|
||||
&& let ExprKind::Call(call, &[arg]) = or_expr.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, call), ResultErr)
|
||||
// And finally we check that it is mapped as `Ok`.
|
||||
&& is_res_lang_ctor(cx, path_res(cx, map_expr), ResultOk)
|
||||
{
|
||||
let msg = "called `map_or(Err(_), Ok)` on an `Option` value";
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
let err_snippet = snippet(cx, arg.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_MAP_OR_ERR_OK,
|
||||
expr.span,
|
||||
msg,
|
||||
"consider using `ok_or`",
|
||||
format!("{self_snippet}.ok_or({err_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
|
|||
if (is_new(fun) && output_type_implements_default(fun))
|
||||
|| match call_expr {
|
||||
Some(call_expr) => is_default_equivalent(cx, call_expr),
|
||||
None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun),
|
||||
None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun),
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
|
||||
Applicability::MachineApplicable,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -20,14 +21,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
|
|||
&& let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind
|
||||
&& SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments)
|
||||
{
|
||||
span_lint(
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
format!(
|
||||
"it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, recv.span, "_")
|
||||
),
|
||||
"using `.zip()` with a range and `.len()`",
|
||||
"try",
|
||||
format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::STDIN;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_local_use_after_expr;
|
||||
use clippy_utils::{get_parent_expr, match_def_path};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
|
@ -34,7 +33,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
|
|||
|
||||
pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& match_def_path(cx, recv_adt.did(), &STDIN)
|
||||
&& cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did())
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
|
|||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner.into(), false, indent_of(cx, expr.span))
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
);
|
||||
|
||||
span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability);
|
||||
|
|
|
|||
25
clippy_lints/src/methods/unbuffered_bytes.rs
Normal file
25
clippy_lints/src/methods/unbuffered_bytes.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use super::UNBUFFERED_BYTES;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
// Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered.
|
||||
if is_trait_method(cx, expr, sym::IoRead)
|
||||
&& let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead)
|
||||
&& let ty = cx.typeck_results().expr_ty_adjusted(recv)
|
||||
&& !implements_trait(cx, ty, buf_read, &[])
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNBUFFERED_BYTES,
|
||||
expr.span,
|
||||
"calling .bytes() is very inefficient when data is not in memory",
|
||||
None,
|
||||
"consider using `BufReader`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -98,10 +98,7 @@ pub(super) fn check(
|
|||
]),
|
||||
("None", "unwrap_or_else", _) => match args[0].kind {
|
||||
hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![
|
||||
(
|
||||
expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()),
|
||||
String::new(),
|
||||
),
|
||||
(expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()), String::new()),
|
||||
(expr.span.with_lo(args[0].span.hi()), String::new()),
|
||||
]),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::{Sugg, make_binop};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id};
|
||||
use rustc_ast::LitKind::Bool;
|
||||
|
|
@ -81,9 +81,11 @@ pub(super) fn check<'a>(
|
|||
&& (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
|
||||
&& !is_local_used(cx, non_binding_location, hir_id)
|
||||
&& let typeck_results = cx.typeck_results()
|
||||
&& typeck_results.expr_ty(l) == typeck_results.expr_ty(r)
|
||||
&& let l_ty = typeck_results.expr_ty(l)
|
||||
&& l_ty == typeck_results.expr_ty(r)
|
||||
&& let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
|
||||
&& implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
|
||||
&& is_copy(cx, l_ty)
|
||||
{
|
||||
let wrap = variant.variant_name();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_in_test, is_lint_allowed};
|
||||
use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, Lint};
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -39,6 +39,7 @@ pub(super) fn check(
|
|||
expr: &Expr<'_>,
|
||||
recv: &Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_unwrap_in_consts: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
variant: Variant,
|
||||
) {
|
||||
|
|
@ -65,6 +66,10 @@ pub(super) fn check(
|
|||
return;
|
||||
}
|
||||
|
||||
if allow_unwrap_in_consts && is_inside_always_const_context(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
variant.lint(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{should_call_clone_as_function, walk_ptrs_ty_depth};
|
||||
use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
|
||||
};
|
||||
|
|
@ -55,12 +55,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
|
||||
let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
|
||||
if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
|
||||
// allow the `as_ref` or `as_mut` if it is followed by another method call
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let hir::ExprKind::MethodCall(segment, ..) = parent.kind
|
||||
&& segment.ident.span != expr.span
|
||||
{
|
||||
return;
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
// allow the `as_ref` or `as_mut` if it is followed by another method call
|
||||
if let hir::ExprKind::MethodCall(segment, ..) = parent.kind
|
||||
&& segment.ident.span != expr.span
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// allow the `as_ref` or `as_mut` if they belong to a closure that changes
|
||||
// the number of references
|
||||
if matches!(parent.kind, hir::ExprKind::Closure(..)) && rcv_depth != res_depth {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
@ -94,6 +101,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
&& is_calling_clone(cx, arg)
|
||||
// And that we are not recommending recv.clone() over Arc::clone() or similar
|
||||
&& !should_call_clone_as_function(cx, rcv_ty)
|
||||
// https://github.com/rust-lang/rust-clippy/issues/12357
|
||||
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(recvr), clone_trait, &[])
|
||||
{
|
||||
lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,8 +121,7 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
|
|||
// Check whether the node is part of a `use` statement. We don't want to emit a warning if the user
|
||||
// has no control over the type.
|
||||
let usenode = opt_as_use_node(node).or_else(|| {
|
||||
cx
|
||||
.tcx
|
||||
cx.tcx
|
||||
.hir_parent_iter(hir_id)
|
||||
.find_map(|(_, node)| opt_as_use_node(node))
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use clippy_utils::{
|
|||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
|
||||
|
|
@ -286,7 +285,8 @@ fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
if name.starts_with('_')
|
||||
&& !name.starts_with("__")
|
||||
&& !definition_span.from_expansion()
|
||||
&& def_id.krate == LOCAL_CRATE
|
||||
&& def_id.is_local()
|
||||
&& !cx.tcx.is_foreign_item(def_id)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
|
@ -13,7 +13,6 @@ use rustc_middle::ty;
|
|||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
|||
|
|
@ -54,8 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
|
|||
);
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(path, receiver, arguments, _) => {
|
||||
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
|
||||
ExprKind::MethodCall(path, receiver, arguments, _)
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) =>
|
||||
{
|
||||
let args = cx.typeck_results().node_args(e.hir_id);
|
||||
let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args);
|
||||
check_arguments(
|
||||
|
|
|
|||
|
|
@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
|
|||
.iter()
|
||||
.map(|stmt| {
|
||||
let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span);
|
||||
let snip = snippet_block(cx, span, "..", None).into_owned();
|
||||
let snip = snippet_block(cx, span, "..", None);
|
||||
snip.lines()
|
||||
.map(|line| format!("{}{line}", " ".repeat(indent)))
|
||||
.collect::<Vec<_>>()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
|
@ -20,7 +21,6 @@ use rustc_session::impl_lint_pass;
|
|||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use clippy_utils::source::{SpanRangeExt, snippet};
|
|||
use clippy_utils::ty::{
|
||||
implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
|
||||
};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
|
|
@ -19,7 +20,6 @@ use rustc_session::declare_lint_pass;
|
|||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{BytePos, Pos};
|
||||
use rustc_abi::ExternAbi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
|||
|
|
@ -180,7 +180,9 @@ impl<'tcx> NonCopyConst<'tcx> {
|
|||
|
||||
fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
// No branch that we check (yet) should continue if val isn't a branch
|
||||
let Some(val) = val.try_to_branch() else { return false };
|
||||
let Some(branched_val) = val.try_to_branch() else {
|
||||
return false;
|
||||
};
|
||||
match *ty.kind() {
|
||||
// the fact that we have to dig into every structs to search enums
|
||||
// leads us to the point checking `UnsafeCell` directly is the only option.
|
||||
|
|
@ -188,11 +190,11 @@ impl<'tcx> NonCopyConst<'tcx> {
|
|||
// As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
|
||||
// contained value.
|
||||
ty::Adt(def, ..) if def.is_union() => false,
|
||||
ty::Array(ty, _) => val
|
||||
ty::Array(ty, _) => branched_val
|
||||
.iter()
|
||||
.any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Adt(def, args) if def.is_enum() => {
|
||||
let Some((&variant_valtree, fields)) = val.split_first() else {
|
||||
let Some((&variant_valtree, fields)) = branched_val.split_first() else {
|
||||
return false;
|
||||
};
|
||||
let variant_index = variant_valtree.unwrap_leaf();
|
||||
|
|
@ -208,14 +210,18 @@ impl<'tcx> NonCopyConst<'tcx> {
|
|||
)
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
|
||||
},
|
||||
ty::Adt(def, args) => val
|
||||
ty::Adt(def, args) => branched_val
|
||||
.iter()
|
||||
.zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Tuple(tys) => val
|
||||
ty::Tuple(tys) => branched_val
|
||||
.iter()
|
||||
.zip(tys)
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
|
||||
Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,8 @@ impl SimilarNamesNameVisitor<'_, '_, '_> {
|
|||
|
||||
fn check_ident(&mut self, ident: Ident) {
|
||||
let interned_name = ident.name.as_str();
|
||||
if interned_name.chars().any(char::is_uppercase) {
|
||||
// name can be empty if it comes from recovery
|
||||
if interned_name.chars().any(char::is_uppercase) || interned_name.is_empty() {
|
||||
return;
|
||||
}
|
||||
if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::{is_in_test, match_def_path, paths};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub struct PanicUnimplemented {
|
||||
allow_panic_in_tests: bool,
|
||||
|
|
@ -137,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
} else if let ExprKind::Call(func, [_]) = expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind
|
||||
&& let Res::Def(DefKind::Fn, def_id) = expr_path.res
|
||||
&& match_def_path(cx, def_id, &paths::PANIC_ANY)
|
||||
&& cx.tcx.is_diagnostic_item(sym::panic_any, def_id)
|
||||
{
|
||||
if cx.tcx.hir_is_inside_const_context(expr.hir_id)
|
||||
|| self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue