Merge from rust-lang/rust

This commit is contained in:
Laurențiu Nicola 2025-03-03 08:38:46 +02:00
commit dd3a5f9a64
3911 changed files with 55521 additions and 32310 deletions

@ -1 +1 @@
Subproject commit 1d1d646c06a84c1aa53967b394b7f1218f85db82
Subproject commit 2622e844bc1e2e6123e54e94e4706f7b6195ce3d

View file

@ -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
@ -5761,6 +5807,7 @@ Released 2018-09-13
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_midpoint`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
@ -5812,6 +5859,7 @@ Released 2018-09-13
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
[`mem_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 +5987,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 +6116,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 +6193,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 +6212,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 +6319,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 +6328,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
@ -6290,6 +6344,7 @@ Released 2018-09-13
[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
[`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros

View file

@ -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"]

View file

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

View file

@ -75,7 +75,7 @@ or if you modify a test file to add a test case.
> _Note:_ This command may update more files than you intended. In that case
> 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`

View file

@ -203,7 +203,7 @@ We'll talk about suggestions more in depth in a [later chapter](emitting_lints.m
Use `cargo bless` to automatically generate the `.fixed` file after running
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

View file

@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
## `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)
@ -394,6 +415,16 @@ For internal testing only, ignores the current `publish` settings in the Cargo m
* [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata)
## `check-incompatible-msrv-in-tests`
Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
**Default Value:** `false`
---
**Affected lints:**
* [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv)
## `check-private-items`
Whether to also run the listed lints on private items.
@ -738,15 +769,21 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
* [`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_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint)
* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
* [`manual_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 +798,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
* [`map_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 +808,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
* [`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)

View file

@ -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

View file

@ -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
@ -457,6 +464,9 @@ define_Conf! {
/// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
#[lints(cargo_common_metadata)]
cargo_ignore_publish: bool = false,
/// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
#[lints(incompatible_msrv)]
check_incompatible_msrv_in_tests: bool = false,
/// Whether to also run the listed lints on private items.
#[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
check_private_items: bool = false,
@ -607,15 +617,21 @@ 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_midpoint,
manual_non_exhaustive,
manual_option_as_slice,
manual_pattern_char_comparison,
manual_range_contains,
manual_rem_euclid,
@ -630,6 +646,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 +656,7 @@ define_Conf! {
ptr_as_ptr,
redundant_field_names,
redundant_static_lifetimes,
repeat_vec_with_capacity,
same_item_push,
seek_from_current,
seek_rewind,
@ -652,7 +670,7 @@ define_Conf! {
unused_trait_names,
use_self,
)]
msrv: Msrv = Msrv::empty(),
msrv: Msrv = Msrv::default(),
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
#[lints(large_types_passed_by_value)]
pass_by_value_size_limit: u64 = 256,

View file

@ -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,
}

View file

@ -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());

View file

@ -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())

View file

@ -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.

View file

@ -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 {
@ -34,7 +35,7 @@ fn main() {
category,
r#type,
msrv,
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
} => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) {
Ok(()) => update_lints::update(utils::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {e}"),
},
@ -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 {
@ -138,9 +147,9 @@ enum DevCommand {
#[command(name = "new_lint")]
/// Create a new lint and run `cargo dev update_lints`
NewLint {
#[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
#[arg(short, long, conflicts_with = "type", default_value = "late")]
/// Specify whether the lint runs during the early or late pass
pass: String,
pass: new_lint::Pass,
#[arg(
short,
long,
@ -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,
},

View file

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

View file

@ -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();
}
}

View file

@ -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"

View file

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

View file

@ -69,13 +69,11 @@ pub struct ApproxConstant {
impl ApproxConstant {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
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 {
@ -89,8 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
_ => (),
}
}
extract_msrv_attr!(LateContext);
}
impl ApproxConstant {
@ -98,7 +94,7 @@ impl ApproxConstant {
let s = s.as_str();
if s.parse::<f64>().is_ok() {
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(msrv)) {
if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(cx, msrv)) {
span_lint_and_help(
cx,
APPROX_CONSTANT,

View file

@ -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

View file

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

View file

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

View file

@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att
span_lint(
cx,
INLINE_ALWAYS,
attr.span,
attr.span(),
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
);
}

View file

@ -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 {

View file

@ -14,7 +14,7 @@ mod useless_attribute;
mod utils;
use clippy_config::Conf;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
use rustc_hir::{ImplItem, Item, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
@ -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"
@ -459,9 +459,7 @@ impl_lint_pass!(Attributes => [
impl Attributes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
@ -471,7 +469,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
if is_relevant_item(cx, item) {
inline_always::check(cx, item.span, item.ident.name, attrs);
}
repr_attributes::check(cx, item.span, attrs, &self.msrv);
repr_attributes::check(cx, item.span, attrs, self.msrv);
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@ -485,18 +483,16 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
}
}
extract_msrv_attr!(LateContext);
}
pub struct EarlyAttributes {
msrv: Msrv,
msrv: MsrvStack,
}
impl EarlyAttributes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
msrv: MsrvStack::new(conf.msrv),
}
}
}
@ -515,17 +511,17 @@ impl EarlyLintPass for EarlyAttributes {
non_minimal_cfg::check(cx, attr);
}
extract_msrv_attr!(EarlyContext);
extract_msrv_attr!();
}
pub struct PostExpansionEarlyAttributes {
msrv: Msrv,
msrv: MsrvStack,
}
impl PostExpansionEarlyAttributes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
msrv: MsrvStack::new(conf.msrv),
}
}
}
@ -589,5 +585,5 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
duplicated_attributes::check(cx, &item.attrs);
}
extract_msrv_attr!(EarlyContext);
extract_msrv_attr!();
}

View file

@ -1,43 +1,39 @@
use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
use rustc_hir::Attribute;
use rustc_lint::LateContext;
use rustc_span::{Span, sym};
use rustc_span::Span;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs;
use clippy_utils::msrvs::{self, Msrv};
use super::REPR_PACKED_WITHOUT_ABI;
pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: &msrvs::Msrv) {
if msrv.meets(msrvs::REPR_RUST) {
check_packed(cx, item_span, attrs);
}
}
pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: Msrv) {
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);
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
if let Some(items) = attrs.iter().find_map(|attr| {
if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) {
attr.meta_item_list()
} else {
None
}
}) && let Some(packed) = items
.iter()
.find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed)))
&& !items.iter().any(|item| {
item.ident()
.is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust))
})
{
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")
if let Some(packed_span) = packed_span
&& !reprs
.iter()
.any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust)
&& msrv.meets(cx, msrvs::REPR_RUST)
{
span_lint_and_then(
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");
},
);
.span_label(packed_span, "`packed` representation set here");
},
);
}
}
}

View file

@ -15,7 +15,7 @@ pub(super) fn check(
) {
if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident()
&& Level::from_symbol(ident.name, Some(attr.id)).is_some()
&& Level::from_symbol(ident.name, || Some(attr.id)).is_some()
&& let Some(items) = behind_cfg_attr.meta_item_list()
{
let nb_items = items.len();

View file

@ -17,7 +17,7 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
}
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
Level::from_symbol(symbol, Some(attr_id)).is_some()
Level::from_symbol(symbol, || Some(attr_id)).is_some()
}
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {

View file

@ -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),
);
}

View file

@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value;
use clippy_utils::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;
@ -84,9 +85,7 @@ pub struct NonminimalBool {
impl NonminimalBool {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
@ -102,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_: Span,
_: LocalDefId,
) {
NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body);
NonminimalBoolVisitor { cx, msrv: self.msrv }.visit_body(body);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@ -119,8 +118,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_ => {},
}
}
extract_msrv_attr!(LateContext);
}
fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
@ -197,7 +194,7 @@ fn check_inverted_bool_in_condition(
);
}
fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
@ -233,7 +230,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
msrv: Msrv,
}
use quine_mc_cluskey::Bool;
@ -326,7 +323,7 @@ impl<'v> Hir2Qmm<'_, '_, 'v> {
struct SuggestContext<'a, 'tcx, 'v> {
terminals: &'v [&'v Expr<'v>],
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
msrv: Msrv,
output: String,
}
@ -353,7 +350,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(_) => {
@ -396,7 +394,7 @@ impl SuggestContext<'_, '_, '_> {
}
}
fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option<String> {
fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Option<String> {
match &expr.kind {
ExprKind::Binary(binop, lhs, rhs) => {
if !implements_ord(cx, lhs) {
@ -438,7 +436,9 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Opti
.iter()
.copied()
.flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
.find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str())
.find(|&(msrv, a, _)| {
a == path.ident.name.as_str() && msrv.is_none_or(|msrv| curr_msrv.meets(cx, msrv))
})
.and_then(|(_, _, neg_method)| {
let negated_args = args
.iter()
@ -467,7 +467,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Opti
}
}
fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
fn suggest(cx: &LateContext<'_>, msrv: Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
let mut suggest_context = SuggestContext {
terminals,
cx,
@ -553,7 +553,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);

View file

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

View file

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

View file

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

View file

@ -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;
}
}

View file

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

View file

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

View file

@ -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"
@ -759,9 +765,7 @@ pub struct Casts {
impl Casts {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
@ -811,8 +815,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) {
return;
}
cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, &self.msrv);
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv);
cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv);
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
@ -825,29 +829,27 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
cast_precision_loss::check(cx, expr, cast_from, cast_to);
cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to);
cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv);
cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to);
}
cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, &self.msrv);
cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, self.msrv);
cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from);
}
as_underscore::check(cx, expr, cast_to_hir);
as_pointer_underscore::check(cx, cast_to, cast_to_hir);
let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR)
&& borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv);
if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted {
let was_borrow_as_ptr_emitted = self.msrv.meets(cx, msrvs::BORROW_AS_PTR)
&& borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv);
if !was_borrow_as_ptr_emitted && self.msrv.meets(cx, msrvs::PTR_FROM_REF) {
ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
}
}
cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr);
ptr_as_ptr::check(cx, expr, &self.msrv);
cast_slice_different_sizes::check(cx, expr, &self.msrv);
ptr_as_ptr::check(cx, expr, self.msrv);
cast_slice_different_sizes::check(cx, expr, self.msrv);
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
}
extract_msrv_attr!(LateContext);
}

View file

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

View file

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

View file

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

View file

@ -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)

View file

@ -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,
@ -602,6 +608,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_DIVISION_INFO,
crate::operators::MANUAL_MIDPOINT_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
crate::operators::MODULO_ARITHMETIC_INFO,
crate::operators::MODULO_ONE_INFO,
@ -684,6 +691,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::single_call_fn::SINGLE_CALL_FN_INFO,
crate::single_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 +749,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,

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
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;
use rustc_middle::ty::{self, FieldDef};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -97,16 +97,7 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR
}
fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
cx.tcx.hir().attrs(hir_id).iter().any(|attr| {
if attr.has_name(sym::repr) {
if let Some(items) = attr.meta_item_list() {
for item in items {
if item.is_word() && matches!(item.name_or_empty(), sym::C) {
return true;
}
}
}
}
false
})
let attrs = cx.tcx.hir().attrs(hir_id);
find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC))
}

View file

@ -40,6 +40,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"),
#[clippy::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`
]}

View file

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

View file

@ -1,9 +1,9 @@
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,
@ -59,7 +59,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.
@ -90,13 +90,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
{

View file

@ -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),
);
}
}

View file

@ -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),
);
}
}

View file

@ -1,18 +1,17 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use rustc_ast::AttrStyle;
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute};
use rustc_errors::Applicability;
use rustc_hir::{AttrArgs, AttrKind, Attribute};
use rustc_lint::LateContext;
use rustc_lint::EarlyContext;
use super::DOC_INCLUDE_WITHOUT_CFG;
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
for attr in attrs {
if !attr.span.from_expansion()
&& let AttrKind::Normal(ref item) = attr.kind
&& attr.doc_str().is_some()
&& let AttrArgs::Eq { expr: meta, .. } = &item.args
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion.

View file

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

View file

@ -20,7 +20,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty;
use rustc_resolve::rustdoc::{
@ -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,6 +600,12 @@ 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);
}
}
impl<'tcx> LateLintPass<'tcx> for Documentation {
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
@ -704,14 +733,13 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
Some(("fake".into(), "fake".into()))
}
include_in_doc_without_cfg::check(cx, attrs);
if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {
return None;
}
let (fragments, _) = attrs_to_doc_fragments(
attrs.iter().filter_map(|attr| {
if 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))
@ -741,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));
@ -764,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.
@ -906,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
@ -1086,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;

View file

@ -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;

View file

@ -1,6 +1,7 @@
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_lint::LateContext;
@ -36,15 +37,19 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
attrs
.iter()
.filter_map(|attr| {
if let Some((sym, com_kind)) = attr.doc_str_and_comment_kind()
&& let AttrStyle::Outer = attr.style
&& let Some(com) = sym.as_str().strip_prefix('!')
if let Attribute::Parsed(AttributeKind::DocComment {
style: AttrStyle::Outer,
kind,
comment,
..
}) = attr
&& let Some(com) = comment.as_str().strip_prefix('!')
{
let sugg = match com_kind {
let sugg = match kind {
CommentKind::Line => format!("//!{com}"),
CommentKind::Block => format!("/*!{com}*/"),
};
Some((attr.span, sugg))
Some((attr.span(), sugg))
} else {
None
}

View file

@ -1,3 +1,4 @@
use rustc_attr_parsing::AttributeKind;
use rustc_errors::Applicability;
use rustc_hir::{Attribute, Item, ItemKind};
use rustc_lint::LateContext;
@ -43,15 +44,19 @@ pub(super) fn check(
let mut should_suggest_empty_doc = false;
for attr in attrs {
if let Some(doc) = attr.doc_str() {
spans.push(attr.span);
let doc = doc.as_str();
if let Attribute::Parsed(AttributeKind::DocComment { span, comment, .. }) = attr {
spans.push(span);
let doc = comment.as_str();
let doc = doc.trim();
if spans.len() == 1 {
// 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;
@ -78,7 +83,7 @@ pub(super) fn check(
&& let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
&& let Some(snippet) = snippet_opt(cx, new_span)
{
let Some(first) = snippet_opt(cx, first_span) else {
let Some(first) = snippet_opt(cx, *first_span) else {
return;
};
let Some(comment_form) = first.get(..3) else {

View file

@ -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}"),

View file

@ -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,

View file

@ -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,

View file

@ -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! {

View file

@ -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

View file

@ -1,26 +1,28 @@
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_attr_parsing::RustcVersion;
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 +52,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 +194,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_msrv_map: FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
}
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_msrv_map = make_ty_msrv_map(tcx);
Self {
format_args,
msrv: conf.msrv.clone(),
msrv: conf.msrv,
ignore_mixed: conf.allow_mixed_uninlined_format_args,
ty_msrv_map,
}
}
}
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
&& is_format_macro(cx, macro_call.def_id)
@ -198,17 +234,17 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
macro_call: &macro_call,
format_args,
ignore_mixed: self.ignore_mixed,
msrv: &self.msrv,
ty_msrv_map: &self.ty_msrv_map,
};
linter.check_templates();
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
if self.msrv.meets(cx, msrvs::FORMAT_ARGS_CAPTURE) {
linter.check_uninlined_args();
}
}
}
extract_msrv_attr!(LateContext);
}
struct FormatArgsExpr<'a, 'tcx> {
@ -217,9 +253,11 @@ struct FormatArgsExpr<'a, 'tcx> {
macro_call: &'a MacroCall,
format_args: &'a rustc_ast::FormatArgs,
ignore_mixed: bool,
msrv: &'a Msrv,
ty_msrv_map: &'a FxHashMap<Ty<'tcx>, Option<RustcVersion>>,
}
impl FormatArgsExpr<'_, '_> {
impl<'tcx> FormatArgsExpr<'_, 'tcx> {
fn check_templates(&self) {
for piece in &self.format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece
@ -237,6 +275,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 +482,33 @@ impl FormatArgsExpr<'_, '_> {
}
}
fn check_unnecessary_debug_formatting(&self, name: Symbol, value: &Expr<'tcx>) {
let cx = self.cx;
if !value.span.from_expansion()
&& !is_from_proc_macro(cx, value)
&& let ty = cx.typeck_results().expr_ty(value)
&& self.can_display_format(ty)
{
let snippet = snippet(cx.sess(), value.span, "..");
span_lint_and_then(
cx,
UNNECESSARY_DEBUG_FORMATTING,
value.span,
format!("unnecessary `Debug` formatting in `{name}!` args"),
|diag| {
diag.help(format!(
"use `Display` formatting and change this to `{snippet}.display()`"
));
diag.note(
"switching to `Display` formatting will change how the value is shown; \
escaped characters will no longer be escaped and surrounding quotes will \
be removed",
);
},
);
}
}
fn format_arg_positions(&self) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
self.format_args.template.iter().flat_map(|piece| match piece {
FormatArgsPiece::Placeholder(placeholder) => {
@ -465,6 +535,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(msrv) = self.ty_msrv_map.get(&ty)
&& msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv))
{
return true;
}
// Even if `ty` is not in `self.ty_msrv_map`, check whether `ty` implements `Deref` with
// a `Target` that is in `self.ty_msrv_map`.
if let Some(deref_trait_id) = self.cx.tcx.lang_items().deref_trait()
&& implements_trait(self.cx, ty, deref_trait_id, &[])
&& let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, "Target")
&& let Some(msrv) = self.ty_msrv_map.get(&target_ty)
&& msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv))
{
return true;
}
false
}
}
fn make_ty_msrv_map(tcx: TyCtxt<'_>) -> FxHashMap<Ty<'_>, Option<RustcVersion>> {
[(sym::OsStr, Some(msrvs::OS_STR_DISPLAY)), (sym::Path, None)]
.into_iter()
.filter_map(|(name, feature)| {
tcx.get_diagnostic_item(name).map(|def_id| {
let ty = Ty::new_adt(tcx, tcx.adt_def(def_id), List::empty());
(ty, feature)
})
})
.collect()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)

View file

@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
.hir()
.attrs(item.hir_id())
.iter()
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span));
.filter(|i| i.is_doc_comment())
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span()));
let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
return;
};

View file

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

View file

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

View file

@ -95,6 +95,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
}
}
// FIXME: needs to be an EARLY LINT. all attribute lints should be
#[allow(clippy::too_many_arguments)]
fn check_needless_must_use(
cx: &LateContext<'_>,
@ -117,38 +118,27 @@ 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.
span_lint_and_then(
// 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.
span_lint_and_help(
cx,
MUST_USE_UNIT,
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
|diag| {
let mut attrs_without_must_use = attrs.to_vec();
attrs_without_must_use.retain(|a| a.id != attr.id);
let sugg_str = attrs_without_must_use
.iter()
.map(|a| {
if a.value_str().is_none() {
return a.name_or_empty().to_string();
}
format!("{} = \"{}\"", a.name_or_empty(), a.value_str().unwrap())
})
.collect::<Vec<_>>()
.join(", ");
diag.span_suggestion(
attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()),
"change these attributes to",
sugg_str,
Applicability::MachineApplicable,
);
},
Some(attr.span()),
"remove `must_use`",
);
}
} else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {

View file

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

View file

@ -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()),

View file

@ -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)
}

View file

@ -54,9 +54,7 @@ pub struct IfThenSomeElseNone {
impl IfThenSomeElseNone {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
@ -79,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
&& !is_else_clause(cx.tcx, expr)
&& !is_in_const_context(cx)
&& !expr.span.in_external_macro(cx.sess().source_map())
&& self.msrv.meets(msrvs::BOOL_THEN)
&& self.msrv.meets(cx, msrvs::BOOL_THEN)
&& !contains_return(then_block.stmts)
{
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
"then_some"
} else {
"then"
@ -94,7 +92,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();
@ -120,6 +118,4 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
);
}
}
extract_msrv_attr!(LateContext);
}

View file

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

View file

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

View file

@ -183,7 +183,7 @@ fn suggestion<'tcx>(
fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span {
if let Some(attr) = tcx.hir().attrs(field.hir_id).first() {
field.span.with_lo(attr.span.lo())
field.span.with_lo(attr.span().lo())
} else {
field.span
}

View file

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

View file

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::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

View file

@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
span_lint_and_then(
cx,
INLINE_FN_WITHOUT_BODY,
attr.span,
attr.span(),
format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
|diag| {
diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable);
},
);
}

View file

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

View file

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

View file

@ -2,11 +2,11 @@ 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_hir::{AttrArgs, AttrKind, Attribute, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, sym};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -52,24 +52,6 @@ impl LargeIncludeFile {
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
impl LargeIncludeFile {
fn emit_lint(&self, cx: &LateContext<'_>, span: Span) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LARGE_INCLUDE_FILE,
span,
"attempted to include a large file",
|diag| {
diag.note(format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
));
},
);
}
}
impl LateLintPass<'_> for LargeIncludeFile {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Lit(lit) = &expr.kind
@ -85,18 +67,32 @@ impl LateLintPass<'_> for LargeIncludeFile {
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
{
self.emit_lint(cx, expr.span.source_callsite());
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LARGE_INCLUDE_FILE,
expr.span.source_callsite(),
"attempted to include a large file",
|diag| {
diag.note(format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
));
},
);
}
}
}
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
impl EarlyLintPass for LargeIncludeFile {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
if !attr.span.from_expansion()
// Currently, rustc limits the usage of macro at the top-level of attributes,
// so we don't need to recurse into each level.
&& let AttrKind::Normal(ref item) = attr.kind
&& let Some(doc) = attr.doc_str()
&& doc.as_str().len() as u64 > self.max_file_size
&& let AttrArgs::Eq { expr: meta, .. } = &item.args
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion.
@ -113,7 +109,19 @@ impl LateLintPass<'_> for LargeIncludeFile {
&& let sub_snippet = sub_snippet.trim()
&& (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
{
self.emit_lint(cx, attr.span);
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LARGE_INCLUDE_FILE,
attr.span,
"attempted to include a large file",
|diag| {
diag.note(format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
));
},
);
}
}
}

View file

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

View file

@ -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
///
@ -717,6 +718,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
@ -840,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));
@ -860,6 +862,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
@ -902,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)));
@ -946,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());
@ -980,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`
}

View file

@ -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
@ -98,13 +130,15 @@ pub struct Lifetimes {
impl Lifetimes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
Self { msrv: conf.msrv }
}
}
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<'_>) {
@ -115,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
..
} = item.kind
{
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv);
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
} else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() {
report_extra_impl_lifetimes(cx, impl_);
@ -134,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
item.generics,
item.span,
report_extra_lifetimes,
&self.msrv,
self.msrv,
);
}
}
@ -145,11 +179,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None),
};
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv);
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, self.msrv);
}
}
extract_msrv_attr!(LateContext);
}
#[allow(clippy::too_many_arguments)]
@ -161,7 +193,7 @@ fn check_fn_inner<'tcx>(
generics: &'tcx Generics<'_>,
span: Span,
report_extra_lifetimes: bool,
msrv: &Msrv,
msrv: Msrv,
) {
if span.in_external_macro(cx.sess().source_map()) || has_where_lifetimes(cx, generics) {
return;
@ -234,7 +266,7 @@ fn could_use_elision<'tcx>(
body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>],
msrv: &Msrv,
msrv: Msrv,
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
@ -352,17 +384,12 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
}
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
fn non_elidable_self_type<'tcx>(
cx: &LateContext<'tcx>,
func: &FnDecl<'tcx>,
ident: Option<Ident>,
msrv: &Msrv,
) -> bool {
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
&& let Some(ident) = ident
fn non_elidable_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>, msrv: Msrv) -> bool {
if let Some(ident) = ident
&& ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first()
&& !msrv.meets(cx, msrvs::EXPLICIT_SELF_TYPE_ELISION)
{
let mut visitor = RefVisitor::new(cx);
visitor.visit_ty_unambig(self_ty);
@ -746,6 +773,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
}
#[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 +799,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(|&lt| cx.tcx.def_span(lt))
@ -785,7 +841,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 +852,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 +892,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)
}

View file

@ -1,12 +1,24 @@
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 }
}
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
@ -55,7 +67,8 @@ 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<'_>) {
@ -65,6 +78,7 @@ impl LateLintPass<'_> for LinesFilterMapOk {
&& matches!(fm_method_str, "filter_map" | "flat_map" | "flatten")
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
&& should_lint(cx, fm_args, fm_method_str)
&& self.msrv.meets(cx, msrvs::MAP_WHILE)
{
span_lint_and_then(
cx,

View file

@ -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;

View file

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

View file

@ -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(cx, msrvs::ITER_FLATTEN)
{
let if_let_type = if some_ctor { "Some" } else { "Ok" };
// Prepare the error message

View file

@ -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};
@ -24,12 +24,8 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
msrv: &Msrv,
msrv: Msrv,
) {
if !msrv.meets(msrvs::SLICE_FILL) {
return;
}
// `for _ in 0..slice.len() { slice[_] = value; }`
if let Some(higher::Range {
start: Some(start),
@ -43,7 +39,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 +50,14 @@ 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)
&& msrv.meets(cx, msrvs::SLICE_FILL)
{
sugg(cx, body, expr, slice.span, assignval.span);
}
@ -73,10 +73,12 @@ 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, &[])
&& msrv.meets(cx, msrvs::SLICE_FILL)
{
sugg(cx, body, expr, recv_path.span, assignval.span);
}

View file

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

View file

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

View file

@ -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,
@ -94,7 +108,7 @@ impl LateLintPass<'_> for MacroUseImports {
{
for kid in cx.tcx.module_children(id) {
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
let span = mac_attr.span;
let span = mac_attr.span();
let def_path = cx.tcx.def_path_str(mac_id);
self.imports.push((def_path, span, hir_id));
}
@ -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);

View file

@ -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());

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -71,7 +71,7 @@ pub struct ManualNonExhaustive {
impl ManualNonExhaustive {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
msrv: conf.msrv,
constructed_enum_variants: FxHashSet::default(),
potential_enums: Vec::new(),
}
@ -82,7 +82,7 @@ impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
if !cx.effective_visibilities.is_exported(item.owner_id.def_id) || !self.msrv.meets(cx, msrvs::NON_EXHAUSTIVE) {
return;
}
@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
if let Some(non_exhaustive) =
attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
{
diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive");
diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive");
} else {
let indent = snippet_indent(cx, item.span).unwrap_or_default();
diag.span_suggestion_verbose(
@ -171,6 +171,4 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
);
}
}
extract_msrv_attr!(LateContext);
}

View file

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

View file

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

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