1
Fork 0

Merge commit 'ac0e10aa68' into clippyup

This commit is contained in:
Philipp Krones 2022-10-06 09:44:38 +02:00
parent da16cc1da9
commit d75b25faab
617 changed files with 10259 additions and 4400 deletions

View file

@ -6,11 +6,161 @@ document.
## Unreleased / In Rust Nightly ## Unreleased / In Rust Nightly
[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master) [3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master)
## Rust 1.64
Current stable, released 2022-09-22
[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
### New Lints
* [`arithmetic_side_effects`]
[#9130](https://github.com/rust-lang/rust-clippy/pull/9130)
* [`invalid_utf8_in_unchecked`]
[#9105](https://github.com/rust-lang/rust-clippy/pull/9105)
* [`assertions_on_result_states`]
[#9225](https://github.com/rust-lang/rust-clippy/pull/9225)
* [`manual_find`]
[#8649](https://github.com/rust-lang/rust-clippy/pull/8649)
* [`manual_retain`]
[#8972](https://github.com/rust-lang/rust-clippy/pull/8972)
* [`default_instead_of_iter_empty`]
[#8989](https://github.com/rust-lang/rust-clippy/pull/8989)
* [`manual_rem_euclid`]
[#9031](https://github.com/rust-lang/rust-clippy/pull/9031)
* [`obfuscated_if_else`]
[#9148](https://github.com/rust-lang/rust-clippy/pull/9148)
* [`std_instead_of_core`]
[#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
* [`std_instead_of_alloc`]
[#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
* [`alloc_instead_of_core`]
[#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
* [`explicit_auto_deref`]
[#8355](https://github.com/rust-lang/rust-clippy/pull/8355)
### Moves and Deprecations
* Moved [`format_push_string`] to `restriction` (now allow-by-default)
[#9161](https://github.com/rust-lang/rust-clippy/pull/9161)
### Enhancements
* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message
[#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
* [`single_match`], [`single_match_else`]: Now catches more `Option` cases
[#8985](https://github.com/rust-lang/rust-clippy/pull/8985)
* [`unused_async`]: Now works for async methods
[#9025](https://github.com/rust-lang/rust-clippy/pull/9025)
* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions
[#8958](https://github.com/rust-lang/rust-clippy/pull/8958)
* [`question_mark`]: Now works for simple `if let` expressions
[#8356](https://github.com/rust-lang/rust-clippy/pull/8356)
* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures
[#9117](https://github.com/rust-lang/rust-clippy/pull/9117)
* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses
[#8703](https://github.com/rust-lang/rust-clippy/pull/8703)
* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks
[#9124](https://github.com/rust-lang/rust-clippy/pull/9124)
* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()`
[#8953](https://github.com/rust-lang/rust-clippy/pull/8953)
* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option
[#9199](https://github.com/rust-lang/rust-clippy/pull/9199)
* [`box_collection`]: Now supports all std collections
[#9170](https://github.com/rust-lang/rust-clippy/pull/9170)
### False Positive Fixes
* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter`
[#9140](https://github.com/rust-lang/rust-clippy/pull/9140)
* [`while_let_loop`]: Now ignores cases when the significant drop order would change
[#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant
drop or variable modifications can affect the conditions
[#9138](https://github.com/rust-lang/rust-clippy/pull/9138)
* [`let_underscore_lock`]: Now ignores bindings that aren't locked
[#8990](https://github.com/rust-lang/rust-clippy/pull/8990)
* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe
pointers are used
[#8639](https://github.com/rust-lang/rust-clippy/pull/8639)
* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value
[#9082](https://github.com/rust-lang/rust-clippy/pull/9082)
* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro
[#9015](https://github.com/rust-lang/rust-clippy/pull/9015)
* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!`
[#9006](https://github.com/rust-lang/rust-clippy/pull/9006)
* [`enum_variant_names`]: Now ignores names with `_` prefixes
[#9032](https://github.com/rust-lang/rust-clippy/pull/9032)
* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified
[#9056](https://github.com/rust-lang/rust-clippy/pull/9056)
* [`match_same_arms`]: Now ignores branches with `todo!`
[#9207](https://github.com/rust-lang/rust-clippy/pull/9207)
* [`assign_op_pattern`]: Ignores cases that break borrowing rules
[#9214](https://github.com/rust-lang/rust-clippy/pull/9214)
* [`extra_unused_lifetimes`]: No longer triggers in derive macros
[#9037](https://github.com/rust-lang/rust-clippy/pull/9037)
* [`mismatching_type_param_order`]: Now ignores complicated generic parameters
[#9146](https://github.com/rust-lang/rust-clippy/pull/9146)
* [`equatable_if_let`]: No longer lints in macros
[#9074](https://github.com/rust-lang/rust-clippy/pull/9074)
* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new`
[#9115](https://github.com/rust-lang/rust-clippy/pull/9115)
* [`needless_borrow`]: Now ignores cases that result in the execution of different traits
[#9096](https://github.com/rust-lang/rust-clippy/pull/9096)
* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers
[#9246](https://github.com/rust-lang/rust-clippy/pull/9246)
### Suggestion Fixes/Improvements
* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds
[#9132](https://github.com/rust-lang/rust-clippy/pull/9132)
* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible
[#8939](https://github.com/rust-lang/rust-clippy/pull/8939)
* [`useless_format`]: Now suggests the correct variable name
[#9237](https://github.com/rust-lang/rust-clippy/pull/9237)
* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call
[#9144](https://github.com/rust-lang/rust-clippy/pull/9144)
* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed
[#9026](https://github.com/rust-lang/rust-clippy/pull/9026)
* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation
[#9099](https://github.com/rust-lang/rust-clippy/pull/9099)
* [`manual_flatten`]: Improved message for long code snippets
[#9156](https://github.com/rust-lang/rust-clippy/pull/9156)
* [`explicit_counter_loop`]: The suggestion is now machine applicable
[#9149](https://github.com/rust-lang/rust-clippy/pull/9149)
* [`needless_borrow`]: Now keeps parentheses around fields, when needed
[#9210](https://github.com/rust-lang/rust-clippy/pull/9210)
* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures
[#9134](https://github.com/rust-lang/rust-clippy/pull/9134)
### ICE Fixes
* Fix ICEs related to `#![feature(generic_const_exprs)]` usage
[#9241](https://github.com/rust-lang/rust-clippy/pull/9241)
* Fix ICEs related to reference lints
[#9093](https://github.com/rust-lang/rust-clippy/pull/9093)
* [`question_mark`]: Fix ICE on zero field tuple structs
[#9244](https://github.com/rust-lang/rust-clippy/pull/9244)
### Documentation Improvements
* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section.
[#9022](https://github.com/rust-lang/rust-clippy/pull/9022)
### Others
* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver
[#9036](https://github.com/rust-lang/rust-clippy/pull/9036)
* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the
minimum supported rust version
[#8774](https://github.com/rust-lang/rust-clippy/pull/8774)
## Rust 1.63 ## Rust 1.63
Current stable, released 2022-08-11 Released 2022-08-11
[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0) [7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
@ -3609,6 +3759,7 @@ Released 2018-09-13
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default
[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
@ -3669,6 +3820,7 @@ Released 2018-09-13
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names [`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
@ -3766,6 +3918,7 @@ Released 2018-09-13
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
@ -3834,6 +3987,7 @@ Released 2018-09-13
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
@ -4124,6 +4278,7 @@ Released 2018-09-13
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.65" version = "0.1.66"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"
@ -23,12 +23,12 @@ path = "src/driver.rs"
[dependencies] [dependencies]
clippy_lints = { path = "clippy_lints" } clippy_lints = { path = "clippy_lints" }
semver = "1.0" semver = "1.0"
rustc_tools_util = { path = "rustc_tools_util" } rustc_tools_util = "0.2.1"
tempfile = { version = "3.2", optional = true } tempfile = { version = "3.2", optional = true }
termize = "0.1" termize = "0.1"
[dev-dependencies] [dev-dependencies]
compiletest_rs = { version = "0.8", features = ["tmp"] } compiletest_rs = { version = "0.9", features = ["tmp"] }
tester = "0.9" tester = "0.9"
regex = "1.5" regex = "1.5"
toml = "0.5" toml = "0.5"
@ -55,7 +55,7 @@ tokio = { version = "1", features = ["io-util"] }
rustc-semver = "1.1" rustc-semver = "1.1"
[build-dependencies] [build-dependencies]
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } rustc_tools_util = "0.2.1"
[features] [features]
deny-warnings = ["clippy_lints/deny-warnings"] deny-warnings = ["clippy_lints/deny-warnings"]

View file

@ -139,25 +139,6 @@ line. (You can swap `clippy::all` with the specific lint category you are target
## Configuration ## Configuration
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
value` mapping e.g.
```toml
avoid-breaking-exported-api = false
disallowed-names = ["toto", "tata", "titi"]
cognitive-complexity-threshold = 30
```
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
lints can be configured and the meaning of the variables.
Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`;
for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that
any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch.
To deactivate the “for further information visit *lint-link*” message you can
define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
### Allowing/denying lints ### Allowing/denying lints
You can add options to your code to `allow`/`warn`/`deny` Clippy lints: You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
@ -205,6 +186,33 @@ the lint(s) you are interested in:
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
``` ```
### Configure the behavior of some lints
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
value` mapping e.g.
```toml
avoid-breaking-exported-api = false
disallowed-names = ["toto", "tata", "titi"]
cognitive-complexity-threshold = 30
```
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
lints can be configured and the meaning of the variables.
> **Note**
>
> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints.
> **Note**
>
> Configuration changes will not apply for code that has already been compiled and cached under `./target/`;
> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure
> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch.
To deactivate the “for further information visit *lint-link*” message you can
define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
### Specifying the minimum supported Rust version ### Specifying the minimum supported Rust version
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by Projects that intend to support old versions of Rust can disable lints pertaining to newer features by

View file

@ -82,16 +82,16 @@ pub fn run(check: bool, verbose: bool) {
fn output_err(err: CliError) { fn output_err(err: CliError) {
match err { match err {
CliError::CommandFailed(command, stderr) => { CliError::CommandFailed(command, stderr) => {
eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr); eprintln!("error: A command failed! `{command}`\nstderr: {stderr}");
}, },
CliError::IoError(err) => { CliError::IoError(err) => {
eprintln!("error: {}", err); eprintln!("error: {err}");
}, },
CliError::RustfmtNotInstalled => { CliError::RustfmtNotInstalled => {
eprintln!("error: rustfmt nightly is not installed."); eprintln!("error: rustfmt nightly is not installed.");
}, },
CliError::WalkDirError(err) => { CliError::WalkDirError(err) => {
eprintln!("error: {}", err); eprintln!("error: {err}");
}, },
CliError::IntellijSetupActive => { CliError::IntellijSetupActive => {
eprintln!( eprintln!(

View file

@ -41,7 +41,7 @@ fn main() {
matches.contains_id("msrv"), matches.contains_id("msrv"),
) { ) {
Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {}", e), Err(e) => eprintln!("Unable to create lint: {e}"),
} }
}, },
Some(("setup", sub_command)) => match sub_command.subcommand() { Some(("setup", sub_command)) => match sub_command.subcommand() {

View file

@ -1,5 +1,5 @@
use crate::clippy_project_root; use crate::clippy_project_root;
use indoc::{indoc, writedoc}; use indoc::{formatdoc, writedoc};
use std::fmt::Write as _; use std::fmt::Write as _;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::prelude::*; use std::io::prelude::*;
@ -23,7 +23,7 @@ impl<T> Context for io::Result<T> {
match self { match self {
Ok(t) => Ok(t), Ok(t) => Ok(t),
Err(e) => { Err(e) => {
let message = format!("{}: {}", text.as_ref(), e); let message = format!("{}: {e}", text.as_ref());
Err(io::Error::new(ErrorKind::Other, message)) Err(io::Error::new(ErrorKind::Other, message))
}, },
} }
@ -72,7 +72,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_contents = get_lint_file_contents(lint, enable_msrv);
let lint_path = format!("clippy_lints/src/{}.rs", lint.name); let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?;
println!("Generated lint file: `{}`", lint_path); println!("Generated lint file: `{lint_path}`");
Ok(()) Ok(())
} }
@ -86,7 +86,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
path.push("src"); path.push("src");
fs::create_dir(&path)?; fs::create_dir(&path)?;
let header = format!("// compile-flags: --crate-name={}", lint_name); let header = format!("// compile-flags: --crate-name={lint_name}");
write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
Ok(()) Ok(())
@ -106,7 +106,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
let test_contents = get_test_file_contents(lint.name, None); let test_contents = get_test_file_contents(lint.name, None);
write_file(lint.project_root.join(&test_path), test_contents)?; write_file(lint.project_root.join(&test_path), test_contents)?;
println!("Generated test file: `{}`", test_path); println!("Generated test file: `{test_path}`");
} }
Ok(()) Ok(())
@ -186,38 +186,36 @@ pub(crate) fn get_stabilization_version() -> String {
} }
fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
let mut contents = format!( let mut contents = formatdoc!(
indoc! {" r#"
#![allow(unused)] #![allow(unused)]
#![warn(clippy::{})] #![warn(clippy::{lint_name})]
fn main() {{ fn main() {{
// test code goes here // test code goes here
}} }}
"}, "#
lint_name
); );
if let Some(header) = header_commands { if let Some(header) = header_commands {
contents = format!("{}\n{}", header, contents); contents = format!("{header}\n{contents}");
} }
contents contents
} }
fn get_manifest_contents(lint_name: &str, hint: &str) -> String { fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
format!( formatdoc!(
indoc! {r#" r#"
# {} # {hint}
[package] [package]
name = "{}" name = "{lint_name}"
version = "0.1.0" version = "0.1.0"
publish = false publish = false
[workspace] [workspace]
"#}, "#
hint, lint_name
) )
} }
@ -238,76 +236,61 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
let name_upper = lint_name.to_uppercase(); let name_upper = lint_name.to_uppercase();
result.push_str(&if enable_msrv { result.push_str(&if enable_msrv {
format!( formatdoc!(
indoc! {" r#"
use clippy_utils::msrvs; use clippy_utils::msrvs;
{pass_import} {pass_import}
use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{{declare_tool_lint, impl_lint_pass}}; use rustc_session::{{declare_tool_lint, impl_lint_pass}};
"}, "#
pass_type = pass_type,
pass_import = pass_import,
context_import = context_import,
) )
} else { } else {
format!( formatdoc!(
indoc! {" r#"
{pass_import} {pass_import}
use rustc_lint::{{{context_import}, {pass_type}}}; use rustc_lint::{{{context_import}, {pass_type}}};
use rustc_session::{{declare_lint_pass, declare_tool_lint}}; use rustc_session::{{declare_lint_pass, declare_tool_lint}};
"}, "#
pass_import = pass_import,
pass_type = pass_type,
context_import = context_import
) )
}); });
let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
result.push_str(&if enable_msrv { result.push_str(&if enable_msrv {
format!( formatdoc!(
indoc! {" r#"
pub struct {name_camel} {{ pub struct {name_camel} {{
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
}}
impl {name_camel} {{
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {{
Self {{ msrv }}
}} }}
}}
impl {name_camel} {{ impl_lint_pass!({name_camel} => [{name_upper}]);
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {{
Self {{ msrv }}
}}
}}
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} {{ // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
extract_msrv_attr!({context_import}); // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
}} // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
"#
// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
// TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
"},
pass_type = pass_type,
pass_lifetimes = pass_lifetimes,
name_upper = name_upper,
name_camel = name_camel,
context_import = context_import,
) )
} else { } else {
format!( formatdoc!(
indoc! {" r#"
declare_lint_pass!({name_camel} => [{name_upper}]); declare_lint_pass!({name_camel} => [{name_upper}]);
impl {pass_type}{pass_lifetimes} for {name_camel} {{}} impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
"}, "#
pass_type = pass_type,
pass_lifetimes = pass_lifetimes,
name_upper = name_upper,
name_camel = name_camel,
) )
}); });
@ -315,8 +298,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
} }
fn get_lint_declaration(name_upper: &str, category: &str) -> String { fn get_lint_declaration(name_upper: &str, category: &str) -> String {
format!( formatdoc!(
indoc! {r#" r#"
declare_clippy_lint! {{ declare_clippy_lint! {{
/// ### What it does /// ### What it does
/// ///
@ -330,15 +313,13 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String {
/// ```rust /// ```rust
/// // example code which does not raise clippy warning /// // example code which does not raise clippy warning
/// ``` /// ```
#[clippy::version = "{version}"] #[clippy::version = "{}"]
pub {name_upper}, pub {name_upper},
{category}, {category},
"default lint description" "default lint description"
}} }}
"#}, "#,
version = get_stabilization_version(), get_stabilization_version(),
name_upper = name_upper,
category = category,
) )
} }
@ -352,7 +333,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
_ => {}, _ => {},
} }
let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty)); let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}"));
assert!( assert!(
ty_dir.exists() && ty_dir.is_dir(), ty_dir.exists() && ty_dir.is_dir(),
"Directory `{}` does not exist!", "Directory `{}` does not exist!",
@ -412,10 +393,10 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
} }
write_file(lint_file_path.as_path(), lint_file_contents)?; write_file(lint_file_path.as_path(), lint_file_contents)?;
println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name); println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name);
println!( println!(
"Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!", "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!",
lint.name, ty lint.name
); );
Ok(()) Ok(())
@ -542,7 +523,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
.chain(std::iter::once(&*lint_name_upper)) .chain(std::iter::once(&*lint_name_upper))
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
{ {
let _ = write!(new_arr_content, "\n {},", ident); let _ = write!(new_arr_content, "\n {ident},");
} }
new_arr_content.push('\n'); new_arr_content.push('\n');

View file

@ -10,8 +10,8 @@ use std::time::{Duration, SystemTime};
/// Panics if the python commands could not be spawned /// Panics if the python commands could not be spawned
pub fn run(port: u16, lint: Option<&String>) -> ! { pub fn run(port: u16, lint: Option<&String>) -> ! {
let mut url = Some(match lint { let mut url = Some(match lint {
None => format!("http://localhost:{}", port), None => format!("http://localhost:{port}"),
Some(lint) => format!("http://localhost:{}/#{}", port, lint), Some(lint) => format!("http://localhost:{port}/#{lint}"),
}); });
loop { loop {

View file

@ -30,10 +30,7 @@ pub fn install_hook(force_override: bool) {
println!("info: the hook can be removed with `cargo dev remove git-hook`"); println!("info: the hook can be removed with `cargo dev remove git-hook`");
println!("git hook successfully installed"); println!("git hook successfully installed");
}, },
Err(err) => eprintln!( Err(err) => eprintln!("error: unable to copy `{HOOK_SOURCE_FILE}` to `{HOOK_TARGET_FILE}` ({err})"),
"error: unable to copy `{}` to `{}` ({})",
HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
),
} }
} }
@ -77,7 +74,7 @@ pub fn remove_hook() {
fn delete_git_hook_file(path: &Path) -> bool { fn delete_git_hook_file(path: &Path) -> bool {
if let Err(err) = fs::remove_file(path) { if let Err(err) = fs::remove_file(path) {
eprintln!("error: unable to delete existing pre-commit git hook ({})", err); eprintln!("error: unable to delete existing pre-commit git hook ({err})");
false false
} else { } else {
true true

View file

@ -60,7 +60,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
path = absolute_path; path = absolute_path;
}, },
Err(err) => { Err(err) => {
eprintln!("error: unable to get the absolute path of rustc ({})", err); eprintln!("error: unable to get the absolute path of rustc ({err})");
return Err(()); return Err(());
}, },
}; };
@ -103,14 +103,14 @@ fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo
fn read_project_file(file_path: &str) -> Result<String, ()> { fn read_project_file(file_path: &str) -> Result<String, ()> {
let path = Path::new(file_path); let path = Path::new(file_path);
if !path.exists() { if !path.exists() {
eprintln!("error: unable to find the file `{}`", file_path); eprintln!("error: unable to find the file `{file_path}`");
return Err(()); return Err(());
} }
match fs::read_to_string(path) { match fs::read_to_string(path) {
Ok(content) => Ok(content), Ok(content) => Ok(content),
Err(err) => { Err(err) => {
eprintln!("error: the file `{}` could not be read ({})", file_path, err); eprintln!("error: the file `{file_path}` could not be read ({err})");
Err(()) Err(())
}, },
} }
@ -124,10 +124,7 @@ fn inject_deps_into_manifest(
) -> std::io::Result<()> { ) -> std::io::Result<()> {
// do not inject deps if we have already done so // do not inject deps if we have already done so
if cargo_toml.contains(RUSTC_PATH_SECTION) { if cargo_toml.contains(RUSTC_PATH_SECTION) {
eprintln!( eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file");
"warn: dependencies are already setup inside {}, skipping file",
manifest_path
);
return Ok(()); return Ok(());
} }
@ -142,11 +139,7 @@ fn inject_deps_into_manifest(
let new_deps = extern_crates.map(|dep| { let new_deps = extern_crates.map(|dep| {
// format the dependencies that are going to be put inside the Cargo.toml // format the dependencies that are going to be put inside the Cargo.toml
format!( format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display())
"{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
dep = dep,
source_path = rustc_source_dir.display()
)
}); });
// format a new [dependencies]-block with the new deps we need to inject // format a new [dependencies]-block with the new deps we need to inject
@ -163,11 +156,11 @@ fn inject_deps_into_manifest(
// etc // etc
let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
// println!("{}", new_manifest); // println!("{new_manifest}");
let mut file = File::create(manifest_path)?; let mut file = File::create(manifest_path)?;
file.write_all(new_manifest.as_bytes())?; file.write_all(new_manifest.as_bytes())?;
println!("info: successfully setup dependencies inside {}", manifest_path); println!("info: successfully setup dependencies inside {manifest_path}");
Ok(()) Ok(())
} }
@ -214,8 +207,8 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
}, },
Err(err) => { Err(err) => {
eprintln!( eprintln!(
"error: unable to open file `{}` to remove rustc dependencies for {} ({})", "error: unable to open file `{}` to remove rustc dependencies for {} ({err})",
project.cargo_file, project.name, err project.cargo_file, project.name
); );
false false
}, },

View file

@ -17,10 +17,7 @@ pub fn install_tasks(force_override: bool) {
println!("info: the task file can be removed with `cargo dev remove vscode-tasks`"); println!("info: the task file can be removed with `cargo dev remove vscode-tasks`");
println!("vscode tasks successfully installed"); println!("vscode tasks successfully installed");
}, },
Err(err) => eprintln!( Err(err) => eprintln!("error: unable to copy `{TASK_SOURCE_FILE}` to `{TASK_TARGET_FILE}` ({err})"),
"error: unable to copy `{}` to `{}` ({})",
TASK_SOURCE_FILE, TASK_TARGET_FILE, err
),
} }
} }
@ -44,23 +41,17 @@ fn check_install_precondition(force_override: bool) -> bool {
return delete_vs_task_file(path); return delete_vs_task_file(path);
} }
eprintln!( eprintln!("error: there is already a `task.json` file inside the `{VSCODE_DIR}` directory");
"error: there is already a `task.json` file inside the `{}` directory",
VSCODE_DIR
);
println!("info: use the `--force-override` flag to override the existing `task.json` file"); println!("info: use the `--force-override` flag to override the existing `task.json` file");
return false; return false;
} }
} else { } else {
match fs::create_dir(vs_dir_path) { match fs::create_dir(vs_dir_path) {
Ok(_) => { Ok(_) => {
println!("info: created `{}` directory for clippy", VSCODE_DIR); println!("info: created `{VSCODE_DIR}` directory for clippy");
}, },
Err(err) => { Err(err) => {
eprintln!( eprintln!("error: the task target directory `{VSCODE_DIR}` could not be created ({err})");
"error: the task target directory `{}` could not be created ({})",
VSCODE_DIR, err
);
}, },
} }
} }
@ -82,7 +73,7 @@ pub fn remove_tasks() {
fn delete_vs_task_file(path: &Path) -> bool { fn delete_vs_task_file(path: &Path) -> bool {
if let Err(err) = fs::remove_file(path) { if let Err(err) = fs::remove_file(path) {
eprintln!("error: unable to delete the existing `tasks.json` file ({})", err); eprintln!("error: unable to delete the existing `tasks.json` file ({err})");
return false; return false;
} }

View file

@ -45,9 +45,8 @@ fn generate_lint_files(
renamed_lints: &[RenamedLint], renamed_lints: &[RenamedLint],
) { ) {
let internal_lints = Lint::internal_lints(lints); let internal_lints = Lint::internal_lints(lints);
let usable_lints = Lint::usable_lints(lints); let mut usable_lints = Lint::usable_lints(lints);
let mut sorted_usable_lints = usable_lints.clone(); usable_lints.sort_by_key(|lint| lint.name.clone());
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
replace_region_in_file( replace_region_in_file(
update_mode, update_mode,
@ -86,7 +85,7 @@ fn generate_lint_files(
) )
.sorted() .sorted()
{ {
writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
} }
}, },
); );
@ -99,7 +98,7 @@ fn generate_lint_files(
"// end lints modules, do not remove this comment, its used in `update_lints`", "// end lints modules, do not remove this comment, its used in `update_lints`",
|res| { |res| {
for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
writeln!(res, "mod {};", lint_mod).unwrap(); writeln!(res, "mod {lint_mod};").unwrap();
} }
}, },
); );
@ -129,7 +128,7 @@ fn generate_lint_files(
for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
let content = gen_lint_group_list(&lint_group, lints.iter()); let content = gen_lint_group_list(&lint_group, lints.iter());
process_file( process_file(
&format!("clippy_lints/src/lib.register_{}.rs", lint_group), &format!("clippy_lints/src/lib.register_{lint_group}.rs"),
update_mode, update_mode,
&content, &content,
); );
@ -190,9 +189,9 @@ fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
if lints.is_empty() { if lints.is_empty() {
return false; return false;
} }
println!("{}", header); println!("{header}");
for lint in lints.iter().sorted() { for lint in lints.iter().sorted() {
println!(" {}", lint); println!(" {lint}");
} }
println!(); println!();
true true
@ -205,16 +204,16 @@ pub fn print_lints() {
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
for (lint_group, mut lints) in grouped_by_lint_group { for (lint_group, mut lints) in grouped_by_lint_group {
println!("\n## {}", lint_group); println!("\n## {lint_group}");
lints.sort_by_key(|l| l.name.clone()); lints.sort_by_key(|l| l.name.clone());
for lint in lints { for lint in lints {
println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc);
} }
} }
println!("there are {} lints", usable_lint_count); println!("there are {usable_lint_count} lints");
} }
/// Runs the `rename_lint` command. /// Runs the `rename_lint` command.
@ -235,10 +234,10 @@ pub fn print_lints() {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn rename(old_name: &str, new_name: &str, uplift: bool) { pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
if let Some((prefix, _)) = old_name.split_once("::") { if let Some((prefix, _)) = old_name.split_once("::") {
panic!("`{}` should not contain the `{}` prefix", old_name, prefix); panic!("`{old_name}` should not contain the `{prefix}` prefix");
} }
if let Some((prefix, _)) = new_name.split_once("::") { if let Some((prefix, _)) = new_name.split_once("::") {
panic!("`{}` should not contain the `{}` prefix", new_name, prefix); panic!("`{new_name}` should not contain the `{prefix}` prefix");
} }
let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
@ -251,14 +250,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
found_new_name = true; found_new_name = true;
} }
} }
let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`"));
let lint = RenamedLint { let lint = RenamedLint {
old_name: format!("clippy::{}", old_name), old_name: format!("clippy::{old_name}"),
new_name: if uplift { new_name: if uplift {
new_name.into() new_name.into()
} else { } else {
format!("clippy::{}", new_name) format!("clippy::{new_name}")
}, },
}; };
@ -266,13 +265,11 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
// case. // case.
assert!( assert!(
!renamed_lints.iter().any(|l| lint.old_name == l.old_name), !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
"`{}` has already been renamed", "`{old_name}` has already been renamed"
old_name
); );
assert!( assert!(
!deprecated_lints.iter().any(|l| lint.old_name == l.name), !deprecated_lints.iter().any(|l| lint.old_name == l.name),
"`{}` has already been deprecated", "`{old_name}` has already been deprecated"
old_name
); );
// Update all lint level attributes. (`clippy::lint_name`) // Update all lint level attributes. (`clippy::lint_name`)
@ -309,14 +306,12 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
if uplift { if uplift {
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
println!( println!(
"`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually."
old_name
); );
} else if found_new_name { } else if found_new_name {
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
println!( println!(
"`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually."
new_name
); );
} else { } else {
// Rename the lint struct and source files sharing a name with the lint. // Rename the lint struct and source files sharing a name with the lint.
@ -327,16 +322,16 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
// Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
if try_rename_file( if try_rename_file(
Path::new(&format!("tests/ui/{}.rs", old_name)), Path::new(&format!("tests/ui/{old_name}.rs")),
Path::new(&format!("tests/ui/{}.rs", new_name)), Path::new(&format!("tests/ui/{new_name}.rs")),
) { ) {
try_rename_file( try_rename_file(
Path::new(&format!("tests/ui/{}.stderr", old_name)), Path::new(&format!("tests/ui/{old_name}.stderr")),
Path::new(&format!("tests/ui/{}.stderr", new_name)), Path::new(&format!("tests/ui/{new_name}.stderr")),
); );
try_rename_file( try_rename_file(
Path::new(&format!("tests/ui/{}.fixed", old_name)), Path::new(&format!("tests/ui/{old_name}.fixed")),
Path::new(&format!("tests/ui/{}.fixed", new_name)), Path::new(&format!("tests/ui/{new_name}.fixed")),
); );
} }
@ -344,8 +339,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
let replacements; let replacements;
let replacements = if lint.module == old_name let replacements = if lint.module == old_name
&& try_rename_file( && try_rename_file(
Path::new(&format!("clippy_lints/src/{}.rs", old_name)), Path::new(&format!("clippy_lints/src/{old_name}.rs")),
Path::new(&format!("clippy_lints/src/{}.rs", new_name)), Path::new(&format!("clippy_lints/src/{new_name}.rs")),
) { ) {
// Edit the module name in the lint list. Note there could be multiple lints. // Edit the module name in the lint list. Note there could be multiple lints.
for lint in lints.iter_mut().filter(|l| l.module == old_name) { for lint in lints.iter_mut().filter(|l| l.module == old_name) {
@ -356,14 +351,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
} else if !lint.module.contains("::") } else if !lint.module.contains("::")
// Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
&& try_rename_file( && try_rename_file(
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)),
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)),
) )
{ {
// Edit the module name in the lint list. Note there could be multiple lints, or none. // Edit the module name in the lint list. Note there could be multiple lints, or none.
let renamed_mod = format!("{}::{}", lint.module, old_name); let renamed_mod = format!("{}::{old_name}", lint.module);
for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
lint.module = format!("{}::{}", lint.module, new_name); lint.module = format!("{}::{new_name}", lint.module);
} }
replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
replacements.as_slice() replacements.as_slice()
@ -379,7 +374,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
} }
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("{} has been successfully renamed", old_name); println!("{old_name} has been successfully renamed");
} }
println!("note: `cargo uitest` still needs to be run to update the test results"); println!("note: `cargo uitest` still needs to be run to update the test results");
@ -408,7 +403,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) {
}); });
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
println!("info: `{}` has successfully been deprecated", name); println!("info: `{name}` has successfully been deprecated");
if reason == DEFAULT_DEPRECATION_REASON { if reason == DEFAULT_DEPRECATION_REASON {
println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
@ -421,7 +416,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) {
let name_upper = name.to_uppercase(); let name_upper = name.to_uppercase();
let (mut lints, deprecated_lints, renamed_lints) = gather_all(); let (mut lints, deprecated_lints, renamed_lints) = gather_all();
let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; }; let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; };
let mod_path = { let mod_path = {
let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
@ -450,7 +445,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
} }
fn remove_test_assets(name: &str) { fn remove_test_assets(name: &str) {
let test_file_stem = format!("tests/ui/{}", name); let test_file_stem = format!("tests/ui/{name}");
let path = Path::new(&test_file_stem); let path = Path::new(&test_file_stem);
// Some lints have their own directories, delete them // Some lints have their own directories, delete them
@ -512,8 +507,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
eprintln!( eprintln!(
"warn: you will have to manually remove any code related to `{}` from `{}`", "warn: you will have to manually remove any code related to `{name}` from `{}`",
name,
path.display() path.display()
); );
@ -528,7 +522,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
content.replace_range(lint.declaration_range.clone(), ""); content.replace_range(lint.declaration_range.clone(), "");
// Remove the module declaration (mod xyz;) // Remove the module declaration (mod xyz;)
let mod_decl = format!("\nmod {};", name); let mod_decl = format!("\nmod {name};");
content = content.replacen(&mod_decl, "", 1); content = content.replacen(&mod_decl, "", 1);
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
@ -621,13 +615,13 @@ fn round_to_fifty(count: usize) -> usize {
fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) { fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
if update_mode == UpdateMode::Check { if update_mode == UpdateMode::Check {
let old_content = let old_content =
fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display()));
if content != old_content { if content != old_content {
exit_with_failure(); exit_with_failure();
} }
} else { } else {
fs::write(&path, content.as_bytes()) fs::write(&path, content.as_bytes())
.unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display()));
} }
} }
@ -731,11 +725,10 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin
let _ = writeln!( let _ = writeln!(
output, output,
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![", "store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![",
group_name
); );
for (module, name) in details { for (module, name) in details {
let _ = writeln!(output, " LintId::of({}::{}),", module, name); let _ = writeln!(output, " LintId::of({module}::{name}),");
} }
output.push_str("])\n"); output.push_str("])\n");
@ -783,7 +776,7 @@ fn gen_register_lint_list<'a>(
if !is_public { if !is_public {
output.push_str(" #[cfg(feature = \"internal\")]\n"); output.push_str(" #[cfg(feature = \"internal\")]\n");
} }
let _ = writeln!(output, " {}::{},", module_name, lint_name); let _ = writeln!(output, " {module_name}::{lint_name},");
} }
output.push_str("])\n"); output.push_str("])\n");
@ -841,7 +834,7 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
for (rel_path, file) in clippy_lints_src_files() { for (rel_path, file) in clippy_lints_src_files() {
let path = file.path(); let path = file.path();
let contents = let contents =
fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
let module = rel_path let module = rel_path
.components() .components()
.map(|c| c.as_os_str().to_str().unwrap()) .map(|c| c.as_os_str().to_str().unwrap())
@ -1050,7 +1043,7 @@ fn remove_line_splices(s: &str) -> String {
.trim_matches('#') .trim_matches('#')
.strip_prefix('"') .strip_prefix('"')
.and_then(|s| s.strip_suffix('"')) .and_then(|s| s.strip_suffix('"'))
.unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); .unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
let mut res = String::with_capacity(s.len()); let mut res = String::with_capacity(s.len());
unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| { unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
if ch.is_ok() { if ch.is_ok() {
@ -1076,10 +1069,10 @@ fn replace_region_in_file(
end: &str, end: &str,
write_replacement: impl FnMut(&mut String), write_replacement: impl FnMut(&mut String),
) { ) {
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
Ok(x) => x, Ok(x) => x,
Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
}; };
match update_mode { match update_mode {
@ -1087,7 +1080,7 @@ fn replace_region_in_file(
UpdateMode::Check => (), UpdateMode::Check => (),
UpdateMode::Change => { UpdateMode::Change => {
if let Err(e) = fs::write(path, new_contents.as_bytes()) { if let Err(e) = fs::write(path, new_contents.as_bytes()) {
panic!("Cannot write to `{}`: {}", path.display(), e); panic!("Cannot write to `{}`: {e}", path.display());
} }
}, },
} }
@ -1135,7 +1128,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
panic!("failed to {} file `{}`: {}", action, name.display(), error) panic!("failed to {action} file `{}`: {error}", name.display())
} }
fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.65" version = "0.1.66"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"

View file

@ -92,7 +92,7 @@ impl ApproxConstant {
cx, cx,
APPROX_CONSTANT, APPROX_CONSTANT,
e.span, e.span,
&format!("approximate value of `{}::consts::{}` found", module, &name), &format!("approximate value of `{module}::consts::{}` found", &name),
None, None,
"consider using the constant directly", "consider using the constant directly",
); );
@ -126,7 +126,7 @@ fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
// The value is a truncated constant // The value is a truncated constant
true true
} else { } else {
let round_const = format!("{:.*}", value.len() - 2, constant); let round_const = format!("{constant:.*}", value.len() - 2);
value == round_const value == round_const
} }
} }

View file

@ -44,7 +44,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr
cx, cx,
lint, lint,
expr.span, expr.span,
&format!("{} x86 assembly syntax used", style), &format!("{style} x86 assembly syntax used"),
None, None,
&format!("use {} x86 assembly syntax", !style), &format!("use {} x86 assembly syntax", !style),
); );
@ -64,6 +64,7 @@ declare_clippy_lint! {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # #![feature(asm)] /// # #![feature(asm)]
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
/// # unsafe { let ptr = "".as_ptr(); /// # unsafe { let ptr = "".as_ptr();
/// # use std::arch::asm; /// # use std::arch::asm;
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
@ -72,6 +73,7 @@ declare_clippy_lint! {
/// Use instead: /// Use instead:
/// ```rust,no_run /// ```rust,no_run
/// # #![feature(asm)] /// # #![feature(asm)]
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
/// # unsafe { let ptr = "".as_ptr(); /// # unsafe { let ptr = "".as_ptr();
/// # use std::arch::asm; /// # use std::arch::asm;
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
@ -103,6 +105,7 @@ declare_clippy_lint! {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # #![feature(asm)] /// # #![feature(asm)]
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
/// # unsafe { let ptr = "".as_ptr(); /// # unsafe { let ptr = "".as_ptr();
/// # use std::arch::asm; /// # use std::arch::asm;
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
@ -111,6 +114,7 @@ declare_clippy_lint! {
/// Use instead: /// Use instead:
/// ```rust,no_run /// ```rust,no_run
/// # #![feature(asm)] /// # #![feature(asm)]
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
/// # unsafe { let ptr = "".as_ptr(); /// # unsafe { let ptr = "".as_ptr();
/// # use std::arch::asm; /// # use std::arch::asm;
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);

View file

@ -60,9 +60,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
cx, cx,
ASSERTIONS_ON_CONSTANTS, ASSERTIONS_ON_CONSTANTS,
macro_call.span, macro_call.span,
&format!("`assert!(false{})` should probably be replaced", assert_arg), &format!("`assert!(false{assert_arg})` should probably be replaced"),
None, None,
&format!("use `panic!({})` or `unreachable!({0})`", panic_arg), &format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"),
); );
} }
} }

View file

@ -69,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
"called `assert!` with `Result::is_ok`", "called `assert!` with `Result::is_ok`",
"replace with", "replace with",
format!( format!(
"{}.unwrap(){}", "{}.unwrap(){semicolon}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
semicolon
), ),
app, app,
); );
@ -84,9 +83,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
"called `assert!` with `Result::is_err`", "called `assert!` with `Result::is_err`",
"replace with", "replace with",
format!( format!(
"{}.unwrap_err(){}", "{}.unwrap_err(){semicolon}",
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
semicolon
), ),
app, app,
); );

View file

@ -541,10 +541,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
cx, cx,
INLINE_ALWAYS, INLINE_ALWAYS,
attr.span, attr.span,
&format!( &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
"you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
name
),
); );
} }
} }
@ -720,7 +717,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
let mut unix_suggested = false; let mut unix_suggested = false;
for (os, span) in mismatched { for (os, span) in mismatched {
let sugg = format!("target_os = \"{}\"", os); let sugg = format!("target_os = \"{os}\"");
diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
if !unix_suggested && is_unix(os) { if !unix_suggested && is_unix(os) {

View file

@ -1,14 +1,15 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{match_def_path, paths};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::GeneratorInteriorTypeCause; use rustc_middle::ty::GeneratorInteriorTypeCause;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span; use rustc_span::{sym, Span};
use crate::utils::conf::DisallowedType; use crate::utils::conf::DisallowedPath;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -171,12 +172,12 @@ impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF,
#[derive(Debug)] #[derive(Debug)]
pub struct AwaitHolding { pub struct AwaitHolding {
conf_invalid_types: Vec<DisallowedType>, conf_invalid_types: Vec<DisallowedPath>,
def_ids: FxHashMap<DefId, DisallowedType>, def_ids: FxHashMap<DefId, DisallowedPath>,
} }
impl AwaitHolding { impl AwaitHolding {
pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self { pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self {
Self { Self {
conf_invalid_types, conf_invalid_types,
def_ids: FxHashMap::default(), def_ids: FxHashMap::default(),
@ -187,11 +188,8 @@ impl AwaitHolding {
impl LateLintPass<'_> for AwaitHolding { impl LateLintPass<'_> for AwaitHolding {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_invalid_types { for conf in &self.conf_invalid_types {
let path = match conf { let segs: Vec<_> = conf.path().split("::").collect();
DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
};
let segs: Vec<_> = path.split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
self.def_ids.insert(id, conf.clone()); self.def_ids.insert(id, conf.clone());
} }
} }
@ -256,29 +254,27 @@ impl AwaitHolding {
} }
} }
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) {
let (type_name, reason) = match disallowed {
DisallowedType::Simple(path) => (path, &None),
DisallowedType::WithReason { path, reason } => (path, reason),
};
span_lint_and_then( span_lint_and_then(
cx, cx,
AWAIT_HOLDING_INVALID_TYPE, AWAIT_HOLDING_INVALID_TYPE,
span, span,
&format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), &format!(
"`{}` may not be held across an `await` point per `clippy.toml`",
disallowed.path()
),
|diag| { |diag| {
if let Some(reason) = reason { if let Some(reason) = disallowed.reason() {
diag.note(reason.clone()); diag.note(reason);
} }
}, },
); );
} }
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
match_def_path(cx, def_id, &paths::MUTEX_GUARD) cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
|| match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
|| match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)

View file

@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr;
use clippy_utils::higher; use clippy_utils::higher;
use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::{for_each_expr, Descend};
use core::ops::ControlFlow;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BlockCheckMode, Expr, ExprKind};
use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -44,39 +45,6 @@ declare_clippy_lint! {
declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]);
struct ExVisitor<'a, 'tcx> {
found_block: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let ExprKind::Closure(&Closure { body, .. }) = expr.kind {
// do not lint if the closure is called using an iterator (see #1141)
if_chain! {
if let Some(parent) = get_parent_expr(self.cx, expr);
if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind;
let caller = self.cx.typeck_results().expr_ty(self_arg);
if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(self.cx, caller, iter_id, &[]);
then {
return;
}
}
let body = self.cx.tcx.hir().body(body);
let ex = &body.value;
if let ExprKind::Block(block, _) = ex.kind {
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
self.found_block = Some(ex);
return;
}
}
}
walk_expr(self, expr);
}
}
const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
instead, move the block or closure higher and bind it with a `let`"; instead, move the block or closure higher and bind it with a `let`";
@ -145,11 +113,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
} }
} }
} else { } else {
let mut visitor = ExVisitor { found_block: None, cx }; let _: Option<!> = for_each_expr(cond, |e| {
walk_expr(&mut visitor, cond); if let ExprKind::Closure(closure) = e.kind {
if let Some(block) = visitor.found_block { // do not lint if the closure is called using an iterator (see #1141)
span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); if_chain! {
} if let Some(parent) = get_parent_expr(cx, e);
if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind;
let caller = cx.typeck_results().expr_ty(self_arg);
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(cx, caller, iter_id, &[]);
then {
return ControlFlow::Continue(Descend::No);
}
}
let body = cx.tcx.hir().body(closure.body);
let ex = &body.value;
if let ExprKind::Block(block, _) = ex.kind {
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE);
return ControlFlow::Continue(Descend::No);
}
}
}
ControlFlow::Continue(Descend::Yes)
});
} }
} }
} }

View file

@ -98,9 +98,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
cx, cx,
BOOL_ASSERT_COMPARISON, BOOL_ASSERT_COMPARISON,
macro_call.span, macro_call.span,
&format!("used `{}!` with a literal bool", macro_name), &format!("used `{macro_name}!` with a literal bool"),
"replace it with", "replace it with",
format!("{}!(..)", non_eq_mac), format!("{non_eq_mac}!(..)"),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }

View file

@ -3,7 +3,7 @@ use rustc_hir::{Block, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg}; use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg};
use rustc_errors::Applicability; use rustc_errors::Applicability;
declare_clippy_lint! { declare_clippy_lint! {
@ -56,13 +56,9 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
&& let Some(then_lit) = int_literal(then) && let Some(then_lit) = int_literal(then)
&& let Some(else_lit) = int_literal(else_) && let Some(else_lit) = int_literal(else_)
{ {
let inverted = if let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
check_int_literal_equals_val(then_lit, 1)
&& check_int_literal_equals_val(else_lit, 0) {
false false
} else if } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
check_int_literal_equals_val(then_lit, 0)
&& check_int_literal_equals_val(else_lit, 1) {
true true
} else { } else {
// Expression isn't boolean, exit // Expression isn't boolean, exit
@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi
None None
} }
} }
fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool {
if let ExprKind::Lit(lit) = &expr.kind
&& let LitKind::Int(val, _) = lit.node
&& val == expected_value
{
true
} else {
false
}
}

View file

@ -263,9 +263,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
} }
.and_then(|op| { .and_then(|op| {
Some(format!( Some(format!(
"{}{}{}", "{}{op}{}",
snippet_opt(cx, lhs.span)?, snippet_opt(cx, lhs.span)?,
op,
snippet_opt(cx, rhs.span)? snippet_opt(cx, rhs.span)?
)) ))
}) })
@ -285,7 +284,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
let path: &str = path.ident.name.as_str(); let path: &str = path.ident.name.as_str();
a == path a == path
}) })
.and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, receiver.span)?, neg_method))) .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
}, },
_ => None, _ => None,
} }

View file

@ -0,0 +1,61 @@
use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id};
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// checks for `Box::new(T::default())`, which is better written as
/// `Box::<T>::default()`.
///
/// ### Why is this bad?
/// First, it's more complex, involving two calls instead of one.
/// Second, `Box::default()` can be faster
/// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box).
///
/// ### Known problems
/// The lint may miss some cases (e.g. Box::new(String::from(""))).
/// On the other hand, it will trigger on cases where the `default`
/// code comes from a macro that does something different based on
/// e.g. target operating system.
///
/// ### Example
/// ```rust
/// let x: Box<String> = Box::new(Default::default());
/// ```
/// Use instead:
/// ```rust
/// let x: Box<String> = Box::default();
/// ```
#[clippy::version = "1.65.0"]
pub BOX_DEFAULT,
perf,
"Using Box::new(T::default()) instead of Box::default()"
}
declare_lint_pass!(BoxDefault => [BOX_DEFAULT]);
impl LateLintPass<'_> for BoxDefault {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Call(box_new, [arg]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind
&& let ExprKind::Call(..) = arg.kind
&& !in_external_macro(cx.sess(), expr.span)
&& expr.span.eq_ctxt(arg.span)
&& seg.ident.name == sym::new
&& path_def_id(cx, ty) == cx.tcx.lang_items().owned_box()
&& is_default_equivalent(cx, arg)
{
span_lint_and_help(
cx,
BOX_DEFAULT,
expr.span,
"`Box::new(_)` of default value",
None,
"use `Box::default()` instead",
);
}
}
}

View file

@ -40,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b
} }
fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) { fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
let message = format!("package `{}` is missing `{}` metadata", package.name, field); let message = format!("package `{}` is missing `{field}` metadata", package.name);
span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
} }

View file

@ -57,10 +57,8 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
}, },
DUMMY_SP, DUMMY_SP,
&format!( &format!(
"the \"{}\" {} in the feature name \"{}\" is {}", "the \"{substring}\" {} in the feature name \"{feature}\" is {}",
substring,
if is_prefix { "prefix" } else { "suffix" }, if is_prefix { "prefix" } else { "suffix" },
feature,
if is_negative { "negative" } else { "redundant" } if is_negative { "negative" } else { "redundant" }
), ),
None, None,

View file

@ -196,7 +196,7 @@ impl LateLintPass<'_> for Cargo {
}, },
Err(e) => { Err(e) => {
for lint in NO_DEPS_LINTS { for lint in NO_DEPS_LINTS {
span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}"));
} }
}, },
} }
@ -212,7 +212,7 @@ impl LateLintPass<'_> for Cargo {
}, },
Err(e) => { Err(e) => {
for lint in WITH_DEPS_LINTS { for lint in WITH_DEPS_LINTS {
span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}"));
} }
}, },
} }

View file

@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
cx, cx,
MULTIPLE_CRATE_VERSIONS, MULTIPLE_CRATE_VERSIONS,
DUMMY_SP, DUMMY_SP,
&format!("multiple versions for dependency `{}`: {}", name, versions), &format!("multiple versions for dependency `{name}`: {versions}"),
); );
} }
} }

View file

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
expr.span, expr.span,
"borrow as raw pointer", "borrow as raw pointer",
"try", "try",
format!("{}::ptr::{}!({})", core_or_std, macro_name, snip), format!("{core_or_std}::ptr::{macro_name}!({snip})"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -41,15 +41,9 @@ pub(super) fn check(
); );
let message = if cast_from.is_bool() { let message = if cast_from.is_bool() {
format!( format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`")
"casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`",
cast_from, cast_to
)
} else { } else {
format!( format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type")
"casting `{}` to `{}` may become silently lossy if you later change the type",
cast_from, cast_to
)
}; };
span_lint_and_sugg( span_lint_and_sugg(
@ -58,7 +52,7 @@ pub(super) fn check(
expr.span, expr.span,
&message, &message,
"try", "try",
format!("{}::from({})", cast_to, sugg), format!("{cast_to}::from({sugg})"),
applicability, applicability,
); );
} }

View file

@ -103,10 +103,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
return; return;
} }
format!( format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
"casting `{}` to `{}` may truncate the value{}",
cast_from, cast_to, suffix,
)
}, },
(ty::Adt(def, _), true) if def.is_enum() => { (ty::Adt(def, _), true) if def.is_enum() => {
@ -142,20 +139,17 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
CAST_ENUM_TRUNCATION, CAST_ENUM_TRUNCATION,
expr.span, expr.span,
&format!( &format!(
"casting `{}::{}` to `{}` will truncate the value{}", "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}",
cast_from, variant.name, cast_to, suffix, variant.name,
), ),
); );
return; return;
} }
format!( format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
"casting `{}` to `{}` may truncate the value{}",
cast_from, cast_to, suffix,
)
}, },
(ty::Float(_), true) => { (ty::Float(_), true) => {
format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
}, },
(ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {

View file

@ -35,10 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
cx, cx,
CAST_POSSIBLE_WRAP, CAST_POSSIBLE_WRAP,
expr.span, expr.span,
&format!( &format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",),
"casting `{}` to `{}` may wrap around the value{}",
cast_from, cast_to, suffix,
),
); );
} }
} }

View file

@ -49,9 +49,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
CAST_PTR_ALIGNMENT, CAST_PTR_ALIGNMENT,
expr.span, expr.span,
&format!( &format!(
"casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)",
cast_from,
cast_to,
from_layout.align.abi.bytes(), from_layout.align.abi.bytes(),
to_layout.align.abi.bytes(), to_layout.align.abi.bytes(),
), ),

View file

@ -14,10 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c
cx, cx,
CAST_SIGN_LOSS, CAST_SIGN_LOSS,
expr.span, expr.span,
&format!( &format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"),
"casting `{}` to `{}` may lose the sign of the value",
cast_from, cast_to
),
); );
} }
} }

View file

@ -35,8 +35,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio
CAST_SLICE_DIFFERENT_SIZES, CAST_SLICE_DIFFERENT_SIZES,
expr.span, expr.span,
&format!( &format!(
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
start_ty.ty, from_size, end_ty.ty, to_size, start_ty.ty, end_ty.ty,
), ),
|diag| { |diag| {
let ptr_snippet = source::snippet(cx, left_cast.span, ".."); let ptr_snippet = source::snippet(cx, left_cast.span, "..");

View file

@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
diag.span_suggestion( diag.span_suggestion(
expr.span, expr.span,
"use a byte literal instead", "use a byte literal instead",
format!("b{}", snippet), format!("b{snippet}"),
applicability, applicability,
); );
} }

View file

@ -25,9 +25,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
cx, cx,
FN_TO_NUMERIC_CAST, FN_TO_NUMERIC_CAST,
expr.span, expr.span,
&format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), &format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
"try", "try",
format!("{} as usize", from_snippet), format!("{from_snippet} as usize"),
applicability, applicability,
); );
} }

View file

@ -23,9 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
cx, cx,
FN_TO_NUMERIC_CAST_ANY, FN_TO_NUMERIC_CAST_ANY,
expr.span, expr.span,
&format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), &format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
"did you mean to invoke the function?", "did you mean to invoke the function?",
format!("{}() as {}", from_snippet, cast_to), format!("{from_snippet}() as {cast_to}"),
applicability, applicability,
); );
}, },

View file

@ -24,12 +24,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
cx, cx,
FN_TO_NUMERIC_CAST_WITH_TRUNCATION, FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
expr.span, expr.span,
&format!( &format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"),
"casting function pointer `{}` to `{}`, which truncates the value",
from_snippet, cast_to
),
"try", "try",
format!("{} as usize", from_snippet), format!("{from_snippet} as usize"),
applicability, applicability,
); );
} }

View file

@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVer
let turbofish = match &cast_to_hir_ty.kind { let turbofish = match &cast_to_hir_ty.kind {
TyKind::Infer => Cow::Borrowed(""), TyKind::Infer => Cow::Borrowed(""),
TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
_ => Cow::Owned(format!("::<{}>", to_pointee_ty)), _ => Cow::Owned(format!("::<{to_pointee_ty}>")),
}; };
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVer
expr.span, expr.span,
"`as` casting between raw pointers without changing its mutability", "`as` casting between raw pointers without changing its mutability",
"try `pointer::cast`, a safer alternative", "try `pointer::cast`, a safer alternative",
format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
applicability, applicability,
); );
} }

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use if_chain::if_chain; use if_chain::if_chain;
@ -30,8 +31,10 @@ pub(super) fn check<'tcx>(
} }
} }
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
if let Some(lit) = get_numeric_literal(cast_expr) { if let Some(lit) = get_numeric_literal(cast_expr) {
let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); let literal_str = &cast_str;
if_chain! { if_chain! {
if let LitKind::Int(n, _) = lit.node; if let LitKind::Int(n, _) = lit.node;
@ -49,12 +52,16 @@ pub(super) fn check<'tcx>(
match lit.node { match lit.node {
LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
return false;
}, },
LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
return false;
},
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {
return false;
}, },
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
| LitKind::Float(_, LitFloatType::Suffixed(_)) | LitKind::Float(_, LitFloatType::Suffixed(_))
if cast_from.kind() == cast_to.kind() => if cast_from.kind() == cast_to.kind() =>
@ -62,48 +69,62 @@ pub(super) fn check<'tcx>(
if let Some(src) = snippet_opt(cx, cast_expr.span) { if let Some(src) = snippet_opt(cx, cast_expr.span) {
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true;
} }
} }
}, },
_ => { _ => {},
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!(
"casting to the same type is unnecessary (`{}` -> `{}`)",
cast_from, cast_to
),
"try",
literal_str,
Applicability::MachineApplicable,
);
return true;
}
},
} }
} }
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
"try",
cast_str,
Applicability::MachineApplicable,
);
return true;
}
false false
} }
fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { fn lint_unnecessary_cast(
cx: &LateContext<'_>,
expr: &Expr<'_>,
raw_literal_str: &str,
cast_from: Ty<'_>,
cast_to: Ty<'_>,
) {
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
let replaced_literal; // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1`
let matchless = if literal_str.contains(['(', ')']) { let literal_str = raw_literal_str
replaced_literal = literal_str.replace(['(', ')'], ""); .replace(['(', ')'], "")
&replaced_literal .trim_end_matches('.')
} else { .to_string();
literal_str // we know need to check if the parent is a method call, to add parenthesis accordingly (eg:
// (-1).foo() instead of -1.foo())
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(..) = parent_expr.kind
&& literal_str.starts_with('-')
{
format!("({literal_str}_{cast_to})")
} else {
format!("{literal_str}_{cast_to}")
}; };
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
UNNECESSARY_CAST, UNNECESSARY_CAST,
expr.span, expr.span,
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"),
"try", "try",
format!("{}_{}", matchless.trim_end_matches('.'), cast_to), sugg,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -2,9 +2,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
item.span, item.span,
"checked cast can be simplified", "checked cast can be simplified",
"try", "try",
format!("{}::try_from({}).is_ok()", to_type, snippet), format!("{to_type}::try_from({snippet}).is_ok()"),
applicability, applicability,
); );
} }
@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
/// Check for `expr >= 0` /// Check for `expr >= 0`
fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> { fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
if_chain! { is_integer_literal(check, 0).then(|| Conversion::new_any(candidate))
if let ExprKind::Lit(ref lit) = &check.kind;
if let LitKind::Int(0, _) = &lit.node;
then {
Some(Conversion::new_any(candidate))
} else {
None
}
}
} }
/// Check for `expr >= (to_type::MIN as from_type)` /// Check for `expr >= (to_type::MIN as from_type)`

View file

@ -3,10 +3,12 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::LimitStack; use clippy_utils::LimitStack;
use core::ops::ControlFlow;
use rustc_ast::ast::Attribute; use rustc_ast::ast::Attribute;
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_hir::{Body, ExprKind, FnDecl, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
@ -61,11 +63,27 @@ impl CognitiveComplexity {
return; return;
} }
let expr = &body.value; let expr = body.value;
let mut cc = 1u64;
let mut returns = 0u64;
let _: Option<!> = for_each_expr(expr, |e| {
match e.kind {
ExprKind::If(_, _, _) => {
cc += 1;
},
ExprKind::Match(_, arms, _) => {
if arms.len() > 1 {
cc += 1;
}
cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
},
ExprKind::Ret(_) => returns += 1,
_ => {},
}
ControlFlow::Continue(())
});
let mut helper = CcHelper { cc: 1, returns: 0 };
helper.visit_expr(expr);
let CcHelper { cc, returns } = helper;
let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_ty = cx.typeck_results().node_type(expr.hir_id);
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) {
returns returns
@ -74,13 +92,12 @@ impl CognitiveComplexity {
(returns / 2) (returns / 2)
}; };
let mut rust_cc = cc;
// prevent degenerate cases where unreachable code contains `return` statements // prevent degenerate cases where unreachable code contains `return` statements
if rust_cc >= ret_adjust { if cc >= ret_adjust {
rust_cc -= ret_adjust; cc -= ret_adjust;
} }
if rust_cc > self.limit.limit() { if cc > self.limit.limit() {
let fn_span = match kind { let fn_span = match kind {
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
FnKind::Closure => { FnKind::Closure => {
@ -107,8 +124,7 @@ impl CognitiveComplexity {
COGNITIVE_COMPLEXITY, COGNITIVE_COMPLEXITY,
fn_span, fn_span,
&format!( &format!(
"the function has a cognitive complexity of ({}/{})", "the function has a cognitive complexity of ({cc}/{})",
rust_cc,
self.limit.limit() self.limit.limit()
), ),
None, None,
@ -141,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
} }
} }
struct CcHelper {
cc: u64,
returns: u64,
}
impl<'tcx> Visitor<'tcx> for CcHelper {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
walk_expr(self, e);
match e.kind {
ExprKind::If(_, _, _) => {
self.cc += 1;
},
ExprKind::Match(_, arms, _) => {
if arms.len() > 1 {
self.cc += 1;
}
self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
},
ExprKind::Ret(_) => self.returns += 1,
_ => {},
}
}
}

View file

@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
cx, cx,
DEFAULT_TRAIT_ACCESS, DEFAULT_TRAIT_ACCESS,
expr.span, expr.span,
&format!("calling `{}` is more clear than this expression", replacement), &format!("calling `{replacement}` is more clear than this expression"),
"try", "try",
replacement, replacement,
Applicability::Unspecified, // First resolve the TODO above Applicability::Unspecified, // First resolve the TODO above
@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.map(|(field, rhs)| { .map(|(field, rhs)| {
// extract and store the assigned value for help message // extract and store the assigned value for help message
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
format!("{}: {}", field, value_snippet) format!("{field}: {value_snippet}")
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", "); .join(", ");
@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.map(ToString::to_string) .map(ToString::to_string)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!("{}::<{}>", adt_def_ty_name, &tys_str) format!("{adt_def_ty_name}::<{}>", &tys_str)
} else { } else {
binding_type.to_string() binding_type.to_string()
} }
@ -235,12 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
let sugg = if ext_with_default { let sugg = if ext_with_default {
if field_list.is_empty() { if field_list.is_empty() {
format!("{}::default()", binding_type) format!("{binding_type}::default()")
} else { } else {
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) format!("{binding_type} {{ {field_list}, ..Default::default() }}")
} }
} else { } else {
format!("{} {{ {} }}", binding_type, field_list) format!("{binding_type} {{ {field_list} }}")
}; };
// span lint once per statement that binds default // span lint once per statement that binds default
@ -250,10 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
first_assign.unwrap().span, first_assign.unwrap().span,
"field assignment outside of initializer for an instance created with Default::default()", "field assignment outside of initializer for an instance created with Default::default()",
Some(local.span), Some(local.span),
&format!( &format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"),
"consider initializing the variable with `{}` and removing relevant reassignments",
sugg
),
); );
self.reassigned_linted.insert(span); self.reassigned_linted.insert(span);
} }

View file

@ -23,7 +23,7 @@ declare_clippy_lint! {
/// let _ = std::iter::empty::<usize>(); /// let _ = std::iter::empty::<usize>();
/// let iter: std::iter::Empty<usize> = std::iter::empty(); /// let iter: std::iter::Empty<usize> = std::iter::empty();
/// ``` /// ```
#[clippy::version = "1.63.0"] #[clippy::version = "1.64.0"]
pub DEFAULT_INSTEAD_OF_ITER_EMPTY, pub DEFAULT_INSTEAD_OF_ITER_EMPTY,
style, style,
"check `std::iter::Empty::default()` and replace with `std::iter::empty()`" "check `std::iter::Empty::default()` and replace with `std::iter::empty()`"

View file

@ -95,8 +95,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
src src
} else { } else {
match lit.node { match lit.node {
LitKind::Int(src, _) => format!("{}", src), LitKind::Int(src, _) => format!("{src}"),
LitKind::Float(src, _) => format!("{}", src), LitKind::Float(src, _) => format!("{src}"),
_ => return, _ => return,
} }
}; };

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{self as hir, HirId, Item, ItemKind}; use rustc_hir::{self as hir, HirId, Item, ItemKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
use rustc_hir_analysis::hir_ty_to_ty;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does

View file

@ -135,7 +135,7 @@ declare_clippy_lint! {
/// let x = String::new(); /// let x = String::new();
/// let y: &str = &x; /// let y: &str = &x;
/// ``` /// ```
#[clippy::version = "1.60.0"] #[clippy::version = "1.64.0"]
pub EXPLICIT_AUTO_DEREF, pub EXPLICIT_AUTO_DEREF,
complexity, complexity,
"dereferencing when the compiler would automatically dereference" "dereferencing when the compiler would automatically dereference"
@ -184,6 +184,7 @@ impl Dereferencing {
} }
} }
#[derive(Debug)]
struct StateData { struct StateData {
/// Span of the top level expression /// Span of the top level expression
span: Span, span: Span,
@ -191,12 +192,14 @@ struct StateData {
position: Position, position: Position,
} }
#[derive(Debug)]
struct DerefedBorrow { struct DerefedBorrow {
count: usize, count: usize,
msg: &'static str, msg: &'static str,
snip_expr: Option<HirId>, snip_expr: Option<HirId>,
} }
#[derive(Debug)]
enum State { enum State {
// Any number of deref method calls. // Any number of deref method calls.
DerefMethod { DerefMethod {
@ -276,10 +279,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
(None, kind) => { (None, kind) => {
let expr_ty = typeck.expr_ty(expr); let expr_ty = typeck.expr_ty(expr);
let (position, adjustments) = walk_parents(cx, expr, self.msrv); let (position, adjustments) = walk_parents(cx, expr, self.msrv);
match kind { match kind {
RefOp::Deref => { RefOp::Deref => {
if let Position::FieldAccess(name) = position if let Position::FieldAccess {
name,
of_union: false,
} = position
&& !ty_contains_field(typeck.expr_ty(sub_expr), name) && !ty_contains_field(typeck.expr_ty(sub_expr), name)
{ {
self.state = Some(( self.state = Some((
@ -451,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
(Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
let position = data.position; let position = data.position;
report(cx, expr, State::DerefedBorrow(state), data); report(cx, expr, State::DerefedBorrow(state), data);
if let Position::FieldAccess(name) = position if let Position::FieldAccess{name, ..} = position
&& !ty_contains_field(typeck.expr_ty(sub_expr), name) && !ty_contains_field(typeck.expr_ty(sub_expr), name)
{ {
self.state = Some(( self.state = Some((
@ -616,14 +621,17 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
} }
/// The position of an expression relative to it's parent. /// The position of an expression relative to it's parent.
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
enum Position { enum Position {
MethodReceiver, MethodReceiver,
/// The method is defined on a reference type. e.g. `impl Foo for &T` /// The method is defined on a reference type. e.g. `impl Foo for &T`
MethodReceiverRefImpl, MethodReceiverRefImpl,
Callee, Callee,
ImplArg(HirId), ImplArg(HirId),
FieldAccess(Symbol), FieldAccess {
name: Symbol,
of_union: bool,
}, // union fields cannot be auto borrowed
Postfix, Postfix,
Deref, Deref,
/// Any other location which will trigger auto-deref to a specific time. /// Any other location which will trigger auto-deref to a specific time.
@ -645,7 +653,10 @@ impl Position {
} }
fn can_auto_borrow(self) -> bool { fn can_auto_borrow(self) -> bool {
matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee) matches!(
self,
Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee
)
} }
fn lint_explicit_deref(self) -> bool { fn lint_explicit_deref(self) -> bool {
@ -657,7 +668,7 @@ impl Position {
Self::MethodReceiver Self::MethodReceiver
| Self::MethodReceiverRefImpl | Self::MethodReceiverRefImpl
| Self::Callee | Self::Callee
| Self::FieldAccess(_) | Self::FieldAccess { .. }
| Self::Postfix => PREC_POSTFIX, | Self::Postfix => PREC_POSTFIX,
Self::ImplArg(_) | Self::Deref => PREC_PREFIX, Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
@ -844,7 +855,10 @@ fn walk_parents<'tcx>(
} }
}) })
}, },
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)), ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess {
name: name.name,
of_union: is_union(cx.typeck_results(), child),
}),
ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
| ExprKind::Index(child, _) | ExprKind::Index(child, _)
@ -865,6 +879,13 @@ fn walk_parents<'tcx>(
(position, adjustments) (position, adjustments)
} }
fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool {
typeck
.expr_ty_adjusted(path_expr)
.ty_adt_def()
.map_or(false, rustc_middle::ty::AdtDef::is_union)
}
fn closure_result_position<'tcx>( fn closure_result_position<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
closure: &'tcx Closure<'_>, closure: &'tcx Closure<'_>,
@ -1308,7 +1329,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
}; };
let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
format!("({})", expr_str) format!("({expr_str})")
} else { } else {
expr_str.into_owned() expr_str.into_owned()
}; };
@ -1322,7 +1343,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
Mutability::Mut => "explicit `deref_mut` method call", Mutability::Mut => "explicit `deref_mut` method call",
}, },
"try this", "try this",
format!("{}{}{}", addr_of_str, deref_str, expr_str), format!("{addr_of_str}{deref_str}{expr_str}"),
app, app,
); );
}, },
@ -1336,7 +1357,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
&& !has_enclosing_paren(&snip) && !has_enclosing_paren(&snip)
&& (expr.precedence().order() < data.position.precedence() || calls_field) && (expr.precedence().order() < data.position.precedence() || calls_field)
{ {
format!("({})", snip) format!("({snip})")
} else { } else {
snip.into() snip.into()
}; };
@ -1379,9 +1400,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
let sugg = let sugg =
if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
format!("{}({})", prefix, snip) format!("{prefix}({snip})")
} else { } else {
format!("{}{}", prefix, snip) format!("{prefix}{snip}")
}; };
diag.span_suggestion(data.span, "try this", sugg, app); diag.span_suggestion(data.span, "try this", sugg, app);
}, },
@ -1460,14 +1481,14 @@ impl Dereferencing {
} else { } else {
pat.always_deref = false; pat.always_deref = false;
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
pat.replacements.push((e.span, format!("&{}", snip))); pat.replacements.push((e.span, format!("&{snip}")));
} }
}, },
_ if !e.span.from_expansion() => { _ if !e.span.from_expansion() => {
// Double reference might be needed at this point. // Double reference might be needed at this point.
pat.always_deref = false; pat.always_deref = false;
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
pat.replacements.push((e.span, format!("&{}", snip))); pat.replacements.push((e.span, format!("&{snip}")));
}, },
// Edge case for macros. The span of the identifier will usually match the context of the // Edge case for macros. The span of the identifier will usually match the context of the
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc

View file

@ -191,7 +191,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "1.63.0"] #[clippy::version = "1.63.0"]
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
style, nursery,
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
} }

View file

@ -0,0 +1,151 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{ExpnId, Span};
use crate::utils::conf;
declare_clippy_lint! {
/// ### What it does
/// Denies the configured macros in clippy.toml
///
/// Note: Even though this lint is warn-by-default, it will only trigger if
/// macros are defined in the clippy.toml file.
///
/// ### Why is this bad?
/// Some macros are undesirable in certain contexts, and it's beneficial to
/// lint for them as needed.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-macros = [
/// # Can use a string as the path of the disallowed macro.
/// "std::print",
/// # Can also use an inline table with a `path` key.
/// { path = "std::println" },
/// # When using an inline table, can add a `reason` for why the macro
/// # is disallowed.
/// { path = "serde::Serialize", reason = "no serializing" },
/// ]
/// ```
/// ```
/// use serde::Serialize;
///
/// // Example code where clippy issues a warning
/// println!("warns");
///
/// // The diagnostic will contain the message "no serializing"
/// #[derive(Serialize)]
/// struct Data {
/// name: String,
/// value: usize,
/// }
/// ```
#[clippy::version = "1.65.0"]
pub DISALLOWED_MACROS,
style,
"use of a disallowed macro"
}
pub struct DisallowedMacros {
conf_disallowed: Vec<conf::DisallowedPath>,
disallowed: DefIdMap<usize>,
seen: FxHashSet<ExpnId>,
}
impl DisallowedMacros {
pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
Self {
conf_disallowed,
disallowed: DefIdMap::default(),
seen: FxHashSet::default(),
}
}
fn check(&mut self, cx: &LateContext<'_>, span: Span) {
if self.conf_disallowed.is_empty() {
return;
}
for mac in macro_backtrace(span) {
if !self.seen.insert(mac.expn) {
return;
}
if let Some(&index) = self.disallowed.get(&mac.def_id) {
let conf = &self.conf_disallowed[index];
span_lint_and_then(
cx,
DISALLOWED_MACROS,
mac.span,
&format!("use of a disallowed macro `{}`", conf.path()),
|diag| {
if let Some(reason) = conf.reason() {
diag.note(&format!("{reason} (from clippy.toml)"));
}
},
);
}
}
}
}
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
impl LateLintPass<'_> for DisallowedMacros {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) {
self.disallowed.insert(id, index);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
self.check(cx, expr.span);
}
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
self.check(cx, stmt.span);
}
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) {
self.check(cx, ty.span);
}
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
self.check(cx, pat.span);
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
self.check(cx, item.span);
self.check(cx, item.vis_span);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) {
self.check(cx, item.span);
self.check(cx, item.vis_span);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
self.check(cx, item.span);
self.check(cx, item.vis_span);
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
self.check(cx, item.span);
}
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
self.check(cx, path.span);
}
}

View file

@ -1,7 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
use rustc_hir::{def::Res, def_id::DefIdMap, Expr, ExprKind}; use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -58,12 +60,12 @@ declare_clippy_lint! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisallowedMethods { pub struct DisallowedMethods {
conf_disallowed: Vec<conf::DisallowedMethod>, conf_disallowed: Vec<conf::DisallowedPath>,
disallowed: DefIdMap<usize>, disallowed: DefIdMap<usize>,
} }
impl DisallowedMethods { impl DisallowedMethods {
pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self { pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
Self { Self {
conf_disallowed, conf_disallowed,
disallowed: DefIdMap::default(), disallowed: DefIdMap::default(),
@ -77,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) {
self.disallowed.insert(id, index); self.disallowed.insert(id, index);
} }
} }
@ -102,11 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
}; };
let msg = format!("use of a disallowed method `{}`", conf.path()); let msg = format!("use of a disallowed method `{}`", conf.path());
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
if let conf::DisallowedMethod::WithReason { if let Some(reason) = conf.reason() {
reason: Some(reason), .. diag.note(&format!("{reason} (from clippy.toml)"));
} = conf
{
diag.note(&format!("{} (from clippy.toml)", reason));
} }
}); });
} }

View file

@ -99,8 +99,7 @@ impl EarlyLintPass for DisallowedScriptIdents {
DISALLOWED_SCRIPT_IDENTS, DISALLOWED_SCRIPT_IDENTS,
span, span,
&format!( &format!(
"identifier `{}` has a Unicode script that is not allowed by configuration: {}", "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}",
symbol_str,
script.full_name() script.full_name()
), ),
); );

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{ use rustc_hir::def::{Namespace, Res};
def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, use rustc_hir::def_id::DefId;
}; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span; use rustc_span::Span;
@ -52,13 +52,13 @@ declare_clippy_lint! {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisallowedTypes { pub struct DisallowedTypes {
conf_disallowed: Vec<conf::DisallowedType>, conf_disallowed: Vec<conf::DisallowedPath>,
def_ids: FxHashMap<DefId, Option<String>>, def_ids: FxHashMap<DefId, Option<String>>,
prim_tys: FxHashMap<PrimTy, Option<String>>, prim_tys: FxHashMap<PrimTy, Option<String>>,
} }
impl DisallowedTypes { impl DisallowedTypes {
pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self { pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
Self { Self {
conf_disallowed, conf_disallowed,
def_ids: FxHashMap::default(), def_ids: FxHashMap::default(),
@ -88,15 +88,9 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_disallowed { for conf in &self.conf_disallowed {
let (path, reason) = match conf { let segs: Vec<_> = conf.path().split("::").collect();
conf::DisallowedType::Simple(path) => (path, None), let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)"));
conf::DisallowedType::WithReason { path, reason } => ( match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
path,
reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
),
};
let segs: Vec<_> = path.split("::").collect();
match clippy_utils::def_path_res(cx, &segs) {
Res::Def(_, id) => { Res::Def(_, id) => {
self.def_ids.insert(id, reason); self.def_ids.insert(id, reason);
}, },
@ -130,7 +124,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
cx, cx,
DISALLOWED_TYPES, DISALLOWED_TYPES,
span, span,
&format!("`{}` is not allowed according to config", name), &format!("`{name}` is not allowed according to config"),
|diag| { |diag| {
if let Some(reason) = reason { if let Some(reason) = reason {
diag.note(reason); diag.note(reason);

View file

@ -198,6 +198,29 @@ declare_clippy_lint! {
"presence of `fn main() {` in code examples" "presence of `fn main() {` in code examples"
} }
declare_clippy_lint! {
/// ### What it does
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
/// outside of code blocks
/// ### Why is this bad?
/// It is likely a typo when defining an intra-doc link
///
/// ### Example
/// ```rust
/// /// See also: ['foo']
/// fn bar() {}
/// ```
/// Use instead:
/// ```rust
/// /// See also: [`foo`]
/// fn bar() {}
/// ```
#[clippy::version = "1.63.0"]
pub DOC_LINK_WITH_QUOTES,
pedantic,
"possible typo for an intra-doc link"
}
#[expect(clippy::module_name_repetitions)] #[expect(clippy::module_name_repetitions)]
#[derive(Clone)] #[derive(Clone)]
pub struct DocMarkdown { pub struct DocMarkdown {
@ -214,9 +237,14 @@ impl DocMarkdown {
} }
} }
impl_lint_pass!(DocMarkdown => impl_lint_pass!(DocMarkdown => [
[DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] DOC_LINK_WITH_QUOTES,
); DOC_MARKDOWN,
MISSING_SAFETY_DOC,
MISSING_ERRORS_DOC,
MISSING_PANICS_DOC,
NEEDLESS_DOCTEST_MAIN
]);
impl<'tcx> LateLintPass<'tcx> for DocMarkdown { impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_crate(&mut self, cx: &LateContext<'tcx>) { fn check_crate(&mut self, cx: &LateContext<'tcx>) {
@ -237,7 +265,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(body.value); fpu.visit_expr(body.value);
lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); lint_for_missing_headers(
cx,
item.def_id.def_id,
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
}, },
hir::ItemKind::Impl(impl_) => { hir::ItemKind::Impl(impl_) => {
@ -287,7 +323,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(body.value); fpu.visit_expr(body.value);
lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); lint_for_missing_headers(
cx,
item.def_id.def_id,
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
} }
} }
@ -416,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span:
(no_stars, sizes) (no_stars, sizes)
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Default)]
struct DocHeaders { struct DocHeaders {
safety: bool, safety: bool,
errors: bool, errors: bool,
@ -460,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
} }
if doc.is_empty() { if doc.is_empty() {
return DocHeaders { return DocHeaders::default();
safety: false,
errors: false,
panics: false,
};
} }
let mut cb = fake_broken_link_callback; let mut cb = fake_broken_link_callback;
@ -505,11 +545,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
use pulldown_cmark::{CodeBlockKind, CowStr}; use pulldown_cmark::{CodeBlockKind, CowStr};
let mut headers = DocHeaders { let mut headers = DocHeaders::default();
safety: false,
errors: false,
panics: false,
};
let mut in_code = false; let mut in_code = false;
let mut in_link = None; let mut in_link = None;
let mut in_heading = false; let mut in_heading = false;
@ -596,6 +632,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
check_code(cx, &text, edition, span); check_code(cx, &text, edition, span);
} }
} else { } else {
check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
// Adjust for the beginning of the current `Event` // Adjust for the beginning of the current `Event`
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
text_to_check.push((text, span)); text_to_check.push((text, span));
@ -606,6 +643,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers headers
} }
fn check_link_quotes(
cx: &LateContext<'_>,
in_link: bool,
trimmed_text: &str,
span: Span,
range: &Range<usize>,
begin: usize,
text_len: usize,
) {
if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
// fix the span to only point at the text within the link
let lo = span.lo() + BytePos::from_usize(range.start - begin);
span_lint(
cx,
DOC_LINK_WITH_QUOTES,
span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
"possible intra-doc link using quotes instead of backticks",
);
}
}
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
Ok(o) => o, Ok(o) => o,
@ -790,7 +848,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
diag.span_suggestion_with_style( diag.span_suggestion_with_style(
span, span,
"try", "try",
format!("`{}`", snippet), format!("`{snippet}`"),
applicability, applicability,
// always show the suggestion in a separate line, since the // always show the suggestion in a separate line, since the
// inline presentation adds another pair of backticks // inline presentation adds another pair of backticks

View file

@ -1,60 +0,0 @@
use clippy_utils::diagnostics::span_lint;
use itertools::Itertools;
use rustc_ast::{AttrKind, Attribute};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
/// outside of code blocks
/// ### Why is this bad?
/// It is likely a typo when defining an intra-doc link
///
/// ### Example
/// ```rust
/// /// See also: ['foo']
/// fn bar() {}
/// ```
/// Use instead:
/// ```rust
/// /// See also: [`foo`]
/// fn bar() {}
/// ```
#[clippy::version = "1.63.0"]
pub DOC_LINK_WITH_QUOTES,
pedantic,
"possible typo for an intra-doc link"
}
declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
impl EarlyLintPass for DocLinkWithQuotes {
fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
if let AttrKind::DocComment(_, symbol) = attr.kind {
if contains_quote_link(symbol.as_str()) {
span_lint(
ctx,
DOC_LINK_WITH_QUOTES,
attr.span,
"possible intra-doc link using quotes instead of backticks",
);
}
}
}
}
fn contains_quote_link(s: &str) -> bool {
let mut in_backticks = false;
let mut found_opening = false;
for c in s.chars().tuple_windows::<(char, char)>() {
match c {
('`', _) => in_backticks = !in_backticks,
('[', '\'') if !in_backticks => found_opening = true,
('\'', ']') if !in_backticks && found_opening => return true,
_ => {},
}
}
false
}

View file

@ -1,7 +1,8 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
use clippy_utils::get_parent_node;
use clippy_utils::is_must_use_func_call; use clippy_utils::is_must_use_func_call;
use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
@ -202,11 +203,13 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
{ {
let arg_ty = cx.typeck_results().expr_ty(arg); let arg_ty = cx.typeck_results().expr_ty(arg);
let is_copy = is_copy(cx, arg_ty);
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
let (lint, msg) = match fn_name { let (lint, msg) = match fn_name {
sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY),
sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY),
sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
span_lint_and_help( span_lint_and_help(
cx, cx,
@ -221,7 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
sym::mem_drop sym::mem_drop
if !(arg_ty.needs_drop(cx.tcx, cx.param_env) if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|| is_must_use_func_call(cx, arg) || is_must_use_func_call(cx, arg)
|| is_must_use_ty(cx, arg_ty)) => || is_must_use_ty(cx, arg_ty)
|| drop_is_single_call_in_arm
) =>
{ {
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY) (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
}, },
@ -236,8 +241,23 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
expr.span, expr.span,
msg, msg,
Some(arg.span), Some(arg.span),
&format!("argument has type `{}`", arg_ty), &format!("argument has type `{arg_ty}`"),
); );
} }
} }
} }
// dropping returned value of a function like in the following snippet is considered idiomatic, see
// #9482 for examples match <var> {
// <pat> => drop(fn_with_side_effect_and_returning_some_value()),
// ..
// }
fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool {
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id);
if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
return body.hir_id == drop_expr.hir_id;
}
}
false
}

View file

@ -113,13 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
), ),
}; };
format!( format!(
"if let {}::{} = {}.entry({}) {} else {}", "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
map_ty.entry_path(), map_ty.entry_path(),
entry_kind,
map_str,
key_str,
then_str,
else_str,
) )
} else { } else {
// if .. { insert } else { insert } // if .. { insert } else { insert }
@ -137,16 +132,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
let indent_str = snippet_indent(cx, expr.span); let indent_str = snippet_indent(cx, expr.span);
let indent_str = indent_str.as_deref().unwrap_or(""); let indent_str = indent_str.as_deref().unwrap_or("");
format!( format!(
"match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
{indent} {entry}::{} => {}\n{indent}}}", {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
map_str,
key_str,
then_entry,
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
else_entry,
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
entry = map_ty.entry_path(), entry = map_ty.entry_path(),
indent = indent_str,
) )
} }
} else { } else {
@ -163,20 +153,16 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
then_search.snippet_occupied(cx, then_expr.span, &mut app) then_search.snippet_occupied(cx, then_expr.span, &mut app)
}; };
format!( format!(
"if let {}::{} = {}.entry({}) {}", "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
map_ty.entry_path(), map_ty.entry_path(),
entry_kind,
map_str,
key_str,
body_str,
) )
} else if let Some(insertion) = then_search.as_single_insertion() { } else if let Some(insertion) = then_search.as_single_insertion() {
let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
if contains_expr.negated { if contains_expr.negated {
if insertion.value.can_have_side_effects() { if insertion.value.can_have_side_effects() {
format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")
} else { } else {
format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) format!("{map_str}.entry({key_str}).or_insert({value_str});")
} }
} else { } else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
@ -186,7 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
} else { } else {
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
if contains_expr.negated { if contains_expr.negated {
format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")
} else { } else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
// This would need to be a different lint. // This would need to be a different lint.

View file

@ -202,12 +202,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
cx, cx,
ENUM_VARIANT_NAMES, ENUM_VARIANT_NAMES,
span, span,
&format!("all variants have the same {}fix: `{}`", what, value), &format!("all variants have the same {what}fix: `{value}`"),
None, None,
&format!( &format!(
"remove the {}fixes and use full paths to \ "remove the {what}fixes and use full paths to \
the variants instead of glob imports", the variants instead of glob imports"
what
), ),
); );
} }

View file

@ -51,9 +51,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
false false
}, },
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => { PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
!etc.as_opt_usize().is_some() && array_rec(a)
}
PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
PatKind::Path(_) | PatKind::Lit(_) => true, PatKind::Path(_) | PatKind::Lit(_) => true,
} }
@ -93,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
"this pattern matching can be expressed using equality", "this pattern matching can be expressed using equality",
"try", "try",
format!( format!(
"{} == {}", "{} == {pat_str}",
snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
pat_str,
), ),
applicability, applicability,
); );

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_hir; use clippy_utils::diagnostics::span_lint_hir;
use rustc_hir::intravisit; use rustc_hir::intravisit;
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::FakeReadCause; use rustc_middle::mir::FakeReadCause;
@ -10,7 +11,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct BoxedLocal { pub struct BoxedLocal {
@ -177,7 +177,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
} }
} }
fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} fn fake_read(
&mut self,
_: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>,
_: FakeReadCause,
_: HirId,
) {
}
} }
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::usage::local_used_after_expr; use clippy_utils::usage::local_used_after_expr;
use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
use if_chain::if_chain; use if_chain::if_chain;
@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::binding::BindingMode;
use rustc_middle::ty::{self, ClosureKind, Ty, TypeVisitable}; use rustc_middle::ty::{self, Ty, TypeVisitable};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -122,15 +122,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
then { then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if_chain! { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[])
if substs.as_closure().kind() == ClosureKind::FnMut; && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); {
then {
// Mutable closure is used after current expr; we cannot consume it. // Mutable closure is used after current expr; we cannot consume it.
snippet = format!("&mut {}", snippet); snippet = format!("&mut {snippet}");
}
} }
diag.span_suggestion( diag.span_suggestion(
expr.span, expr.span,
@ -157,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
diag.span_suggestion( diag.span_suggestion(
expr.span, expr.span,
"replace the closure with the method itself", "replace the closure with the method itself",
format!("{}::{}", name, path.ident.name), format!("{name}::{}", path.ident.name),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
}) })

View file

@ -97,7 +97,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
item.span, item.span,
msg, msg,
|diag| { |diag| {
let sugg = format!("#[non_exhaustive]\n{}", indent); let sugg = format!("#[non_exhaustive]\n{indent}");
diag.span_suggestion(suggestion_span, diag.span_suggestion(suggestion_span,
"try adding #[non_exhaustive]", "try adding #[non_exhaustive]",
sugg, sugg,

View file

@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
// used. // used.
let (used, sugg_mac) = if let Some(macro_name) = calling_macro { let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
( (
format!("{}!({}(), ...)", macro_name, dest_name), format!("{macro_name}!({dest_name}(), ...)"),
macro_name.replace("write", "print"), macro_name.replace("write", "print"),
) )
} else { } else {
( (
format!("{}().write_fmt(...)", dest_name), format!("{dest_name}().write_fmt(...)"),
"print".into(), "print".into(),
) )
}; };
@ -100,9 +100,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
cx, cx,
EXPLICIT_WRITE, EXPLICIT_WRITE,
expr.span, expr.span,
&format!("use of `{}.unwrap()`", used), &format!("use of `{used}.unwrap()`"),
"try this", "try this",
format!("{}{}!({})", prefix, sugg_mac, inputs_snippet), format!("{prefix}{sugg_mac}!({inputs_snippet})"),
applicability, applicability,
) )
} }

View file

@ -173,9 +173,9 @@ impl FloatFormat {
T: fmt::UpperExp + fmt::LowerExp + fmt::Display, T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
{ {
match self { match self {
Self::LowerExp => format!("{:e}", f), Self::LowerExp => format!("{f:e}"),
Self::UpperExp => format!("{:E}", f), Self::UpperExp => format!("{f:E}"),
Self::Normal => format!("{}", f), Self::Normal => format!("{f}"),
} }
} }
} }

View file

@ -142,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
then { then {
let op = format!( let op = format!(
"{}{}{}", "{suggestion}{}{}",
suggestion,
// Check for float literals without numbers following the decimal // Check for float literals without numbers following the decimal
// separator such as `2.` and adds a trailing zero // separator such as `2.` and adds a trailing zero
if sym.as_str().ends_with('.') { if sym.as_str().ends_with('.') {
@ -172,7 +171,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar
expr.span, expr.span,
"logarithm for bases 2, 10 and e can be computed more accurately", "logarithm for bases 2, 10 and e can be computed more accurately",
"consider using", "consider using",
format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method), format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
@ -251,7 +250,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
expr.span, expr.span,
"exponent for bases 2 and e can be computed more accurately", "exponent for bases 2 and e can be computed more accurately",
"consider using", "consider using",
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method), format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
if let ExprKind::Binary( if let ExprKind::Binary(
Spanned { Spanned {
node: BinOpKind::Add, .. node: op @ (BinOpKind::Add | BinOpKind::Sub),
..
}, },
lhs, lhs,
rhs, rhs,
@ -320,6 +320,16 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
{ {
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
// Negate expr if original code has subtraction and expr is on the right side
let maybe_neg_sugg = |expr, hir_id| {
let sugg = Sugg::hir(cx, expr, "..");
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
format!("-{sugg}")
} else {
sugg.to_string()
}
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SUBOPTIMAL_FLOPS, SUBOPTIMAL_FLOPS,
@ -329,8 +339,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
format!( format!(
"{}.mul_add({}, {})", "{}.mul_add({}, {})",
Sugg::hir(cx, receiver, "..").maybe_par(), Sugg::hir(cx, receiver, "..").maybe_par(),
Sugg::hir(cx, receiver, ".."), maybe_neg_sugg(receiver, expr.hir_id),
Sugg::hir(cx, other_addend, ".."), maybe_neg_sugg(other_addend, other_addend.hir_id),
), ),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
@ -444,7 +454,8 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Binary( if let ExprKind::Binary(
Spanned { Spanned {
node: BinOpKind::Add, .. node: op @ (BinOpKind::Add | BinOpKind::Sub),
..
}, },
lhs, lhs,
rhs, rhs,
@ -458,10 +469,27 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
} }
} }
let maybe_neg_sugg = |expr| {
let sugg = Sugg::hir(cx, expr, "..");
if let BinOpKind::Sub = op {
format!("-{sugg}")
} else {
sugg.to_string()
}
};
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
(inner_lhs, inner_rhs, rhs) (
inner_lhs,
Sugg::hir(cx, inner_rhs, "..").to_string(),
maybe_neg_sugg(rhs),
)
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
(inner_lhs, inner_rhs, lhs) (
inner_lhs,
maybe_neg_sugg(inner_rhs),
Sugg::hir(cx, lhs, "..").to_string(),
)
} else { } else {
return; return;
}; };
@ -472,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
expr.span, expr.span,
"multiply and add expressions can be calculated more efficiently and accurately", "multiply and add expressions can be calculated more efficiently and accurately",
"consider using", "consider using",
format!( format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)),
"{}.mul_add({}, {})",
prepare_receiver_sugg(cx, recv),
Sugg::hir(cx, arg1, ".."),
Sugg::hir(cx, arg2, ".."),
),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
[_] => { [_] => {
// Simulate macro expansion, converting {{ and }} to { and }. // Simulate macro expansion, converting {{ and }} to { and }.
let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}"); let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
let sugg = format!("{}.to_string()", s_expand); let sugg = format!("{s_expand}.to_string()");
span_useless_format(cx, call_site, sugg, applicability); span_useless_format(cx, call_site, sugg, applicability);
}, },
[..] => {}, [..] => {},

View file

@ -1,16 +1,18 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
use clippy_utils::macros::{is_format_macro, FormatArgsExpn}; use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
use if_chain::if_chain; use if_chain::if_chain;
use itertools::Itertools; use itertools::Itertools;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId}; use rustc_hir::{Expr, ExprKind, HirId, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
@ -64,7 +66,67 @@ declare_clippy_lint! {
"`to_string` applied to a type that implements `Display` in format args" "`to_string` applied to a type that implements `Display` in format args"
} }
declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); declare_clippy_lint! {
/// ### What it does
/// Detect when a variable is not inlined in a format string,
/// and suggests to inline it.
///
/// ### Why is this bad?
/// Non-inlined code is slightly more difficult to read and understand,
/// as it requires arguments to be matched against the format string.
/// The inlined syntax, where allowed, is simpler.
///
/// ### Example
/// ```rust
/// # let var = 42;
/// # let width = 1;
/// # let prec = 2;
/// format!("{}", var);
/// format!("{v:?}", v = var);
/// format!("{0} {0}", var);
/// format!("{0:1$}", var, width);
/// format!("{:.*}", prec, var);
/// ```
/// Use instead:
/// ```rust
/// # let var = 42;
/// # let width = 1;
/// # let prec = 2;
/// format!("{var}");
/// format!("{var:?}");
/// format!("{var} {var}");
/// format!("{var:width$}");
/// format!("{var:.prec$}");
/// ```
///
/// ### Known Problems
///
/// There may be a false positive if the format string is expanded from certain proc macros:
///
/// ```ignore
/// println!(indoc!("{}"), var);
/// ```
///
/// If a format string contains a numbered argument that cannot be inlined
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
#[clippy::version = "1.65.0"]
pub UNINLINED_FORMAT_ARGS,
pedantic,
"using non-inlined variables in `format!` calls"
}
impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]);
pub struct FormatArgs {
msrv: Option<RustcVersion>,
}
impl FormatArgs {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@ -86,9 +148,72 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
check_to_string_in_format_args(cx, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value);
} }
if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
check_uninlined_args(cx, &format_args, outermost_expn_data.call_site);
}
} }
} }
} }
extract_msrv_attr!(LateContext);
}
fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) {
if args.format_string.span.from_expansion() {
return;
}
let mut fixes = Vec::new();
// If any of the arguments are referenced by an index number,
// and that argument is not a simple variable and cannot be inlined,
// we cannot remove any other arguments in the format string,
// because the index numbers might be wrong after inlining.
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
return;
}
// FIXME: Properly ignore a rare case where the format string is wrapped in a macro.
// Example: `format!(indoc!("{}"), foo);`
// If inlined, they will cause a compilation error:
// > to avoid ambiguity, `format_args!` cannot capture variables
// > when the format string is expanded from a macro
// @Alexendoo explanation:
// > indoc! is a proc macro that is producing a string literal with its span
// > set to its input it's not marked as from expansion, and since it's compatible
// > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either
// This might be a relatively expensive test, so do it only we are ready to replace.
// See more examples in tests/ui/uninlined_format_args.rs
span_lint_and_then(
cx,
UNINLINED_FORMAT_ARGS,
call_site,
"variables can be used directly in the `format!` string",
|diag| {
diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
},
);
}
fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
&& let [segment] = path.segments
&& let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
{
let replacement = match param.usage {
FormatParamUsage::Argument => segment.ident.name.to_string(),
FormatParamUsage::Width => format!("{}$", segment.ident.name),
FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
};
fixes.push((param.span, replacement));
fixes.push((arg_span, String::new()));
true // successful inlining, continue checking
} else {
// if we can't inline a numbered argument, we can't continue
param.kind != Numbered
}
} }
fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
@ -117,11 +242,10 @@ fn check_format_in_format_args(
cx, cx,
FORMAT_IN_FORMAT_ARGS, FORMAT_IN_FORMAT_ARGS,
call_site, call_site,
&format!("`format!` in `{}!` args", name), &format!("`format!` in `{name}!` args"),
|diag| { |diag| {
diag.help(&format!( diag.help(&format!(
"combine the `format!(..)` arguments with the outer `{}!(..)` call", "combine the `format!(..)` arguments with the outer `{name}!(..)` call"
name
)); ));
diag.help("or consider changing `format!` to `format_args!`"); diag.help("or consider changing `format!` to `format_args!`");
}, },
@ -149,8 +273,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
TO_STRING_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS,
value.span.with_lo(receiver.span.hi()), value.span.with_lo(receiver.span.hi()),
&format!( &format!(
"`to_string` applied to a type that implements `Display` in `{}!` args", "`to_string` applied to a type that implements `Display` in `{name}!` args"
name
), ),
"remove this", "remove this",
String::new(), String::new(),
@ -162,16 +285,13 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
TO_STRING_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS,
value.span, value.span,
&format!( &format!(
"`to_string` applied to a type that implements `Display` in `{}!` args", "`to_string` applied to a type that implements `Display` in `{name}!` args"
name
), ),
"use this", "use this",
format!( format!(
"{}{:*>width$}{}", "{}{:*>n_needed_derefs$}{receiver_snippet}",
if needs_ref { "&" } else { "" }, if needs_ref { "&" } else { "" },
"", ""
receiver_snippet,
width = n_needed_derefs
), ),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
@ -180,7 +300,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
} }
} }
// Returns true if `hir_id` is referred to by multiple format params /// Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err() args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
} }

View file

@ -214,12 +214,12 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
cx, cx,
PRINT_IN_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL,
macro_call.span, macro_call.span,
&format!("use of `{}!` in `{}` impl", name, impl_trait.name), &format!("use of `{name}!` in `{}` impl", impl_trait.name),
"replace with", "replace with",
if let Some(formatter_name) = impl_trait.formatter_name { if let Some(formatter_name) = impl_trait.formatter_name {
format!("{}!({}, ..)", replacement, formatter_name) format!("{replacement}!({formatter_name}, ..)")
} else { } else {
format!("{}!(..)", replacement) format!("{replacement}!(..)")
}, },
Applicability::HasPlaceholders, Applicability::HasPlaceholders,
); );

View file

@ -154,11 +154,10 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
eqop_span, eqop_span,
&format!( &format!(
"this looks like you are trying to use `.. {op}= ..`, but you \ "this looks like you are trying to use `.. {op}= ..`, but you \
really are doing `.. = ({op} ..)`", really are doing `.. = ({op} ..)`"
op = op
), ),
None, None,
&format!("to remove this lint, use either `{op}=` or `= {op}`", op = op), &format!("to remove this lint, use either `{op}=` or `= {op}`"),
); );
} }
} }
@ -191,16 +190,12 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
SUSPICIOUS_UNARY_OP_FORMATTING, SUSPICIOUS_UNARY_OP_FORMATTING,
eqop_span, eqop_span,
&format!( &format!(
"by not having a space between `{binop}` and `{unop}` it looks like \ "by not having a space between `{binop_str}` and `{unop_str}` it looks like \
`{binop}{unop}` is a single operator", `{binop_str}{unop_str}` is a single operator"
binop = binop_str,
unop = unop_str
), ),
None, None,
&format!( &format!(
"put a space between `{binop}` and `{unop}` and remove the space after `{unop}`", "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"
binop = binop_str,
unop = unop_str
), ),
); );
} }
@ -246,12 +241,11 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
cx, cx,
SUSPICIOUS_ELSE_FORMATTING, SUSPICIOUS_ELSE_FORMATTING,
else_span, else_span,
&format!("this is an `else {}` but the formatting might hide it", else_desc), &format!("this is an `else {else_desc}` but the formatting might hide it"),
None, None,
&format!( &format!(
"to remove this lint, remove the `else` or remove the new line between \ "to remove this lint, remove the `else` or remove the new line between \
`else` and `{}`", `else` and `{else_desc}`",
else_desc,
), ),
); );
} }
@ -320,11 +314,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
cx, cx,
SUSPICIOUS_ELSE_FORMATTING, SUSPICIOUS_ELSE_FORMATTING,
else_span, else_span,
&format!("this looks like {} but the `else` is missing", looks_like), &format!("this looks like {looks_like} but the `else` is missing"),
None, None,
&format!( &format!(
"to remove this lint, add the missing `else` or add a new line before {}", "to remove this lint, add the missing `else` or add a new line before {next_thing}",
next_thing,
), ),
); );
} }

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_integer_literal;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain; use if_chain::if_chain;
@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
if pathseg.ident.name.as_str() == "from_str_radix"; if pathseg.ident.name.as_str() == "from_str_radix";
// check if the second argument is a primitive `10` // check if the second argument is a primitive `10`
if let ExprKind::Lit(lit) = &radix.kind; if is_integer_literal(radix, 10);
if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
then { then {
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
exp.span, exp.span,
"this call to `from_str_radix` can be replaced with a call to `str::parse`", "this call to `from_str_radix` can be replaced with a call to `str::parse`",
"try", "try",
format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), format!("{sugg}.parse::<{}>()", prim_ty.name_str()),
Applicability::MaybeIncorrect Applicability::MaybeIncorrect
); );
} }

View file

@ -1,7 +1,7 @@
use rustc_ast::ast::Attribute; use rustc_ast::ast::Attribute;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::{DefIdSet, LocalDefId}; use rustc_hir::def_id::{DefIdSet, LocalDefId};
use rustc_hir::{self as hir, def::Res, intravisit, QPath}; use rustc_hir::{self as hir, def::Res, QPath};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::{ use rustc_middle::{
lint::in_external_macro, lint::in_external_macro,
@ -13,8 +13,11 @@ use clippy_utils::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_must_use_ty; use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
use core::ops::ControlFlow;
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
@ -47,7 +50,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
if let Some(attr) = attr { if let Some(attr) = attr {
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() { } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none()
{
check_must_use_candidate( check_must_use_candidate(
cx, cx,
sig.decl, sig.decl,
@ -143,7 +147,7 @@ fn check_must_use_candidate<'tcx>(
diag.span_suggestion( diag.span_suggestion(
fn_span, fn_span,
"add the attribute", "add the attribute",
format!("#[must_use] {}", snippet), format!("#[must_use] {snippet}"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
@ -199,63 +203,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m
} }
} }
struct StaticMutVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
mutates_static: bool,
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
if self.mutates_static {
return;
}
match expr.kind {
Call(_, args) => {
let mut tys = DefIdSet::default();
for arg in args {
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty(
self.cx,
self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg)
{
self.mutates_static = true;
return;
}
tys.clear();
}
},
MethodCall(_, receiver, args, _) => {
let mut tys = DefIdSet::default();
for arg in std::iter::once(receiver).chain(args.iter()) {
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty(
self.cx,
self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg)
{
self.mutates_static = true;
return;
}
tys.clear();
}
},
Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
self.mutates_static |= is_mutated_static(target);
},
_ => {},
}
}
}
fn is_mutated_static(e: &hir::Expr<'_>) -> bool { fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
use hir::ExprKind::{Field, Index, Path}; use hir::ExprKind::{Field, Index, Path};
@ -268,10 +215,53 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
} }
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
let mut v = StaticMutVisitor { for_each_expr(body.value, |e| {
cx, use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
mutates_static: false,
}; match e.kind {
intravisit::walk_expr(&mut v, body.value); Call(_, args) => {
v.mutates_static let mut tys = DefIdSet::default();
for arg in args {
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty(
cx,
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg)
{
return ControlFlow::Break(());
}
tys.clear();
}
ControlFlow::Continue(())
},
MethodCall(_, receiver, args, _) => {
let mut tys = DefIdSet::default();
for arg in std::iter::once(receiver).chain(args.iter()) {
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty(
cx,
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
arg.span,
&mut tys,
)
&& is_mutated_static(arg)
{
return ControlFlow::Break(());
}
tys.clear();
}
ControlFlow::Continue(())
},
Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target)
if is_mutated_static(target) =>
{
ControlFlow::Break(())
},
_ => ControlFlow::Continue(()),
}
})
.is_some()
} }

View file

@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::type_is_unsafe_function; use clippy_utils::ty::type_is_unsafe_function;
use clippy_utils::visitors::for_each_expr_with_closures;
use clippy_utils::{iter_input_pats, path_to_local}; use clippy_utils::{iter_input_pats, path_to_local};
use core::ops::ControlFlow;
use super::NOT_UNSAFE_PTR_ARG_DEREF; use super::NOT_UNSAFE_PTR_ARG_DEREF;
pub(super) fn check_fn<'tcx>( pub(super) fn check_fn<'tcx>(
@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>(
body: &'tcx hir::Body<'tcx>, body: &'tcx hir::Body<'tcx>,
def_id: LocalDefId, def_id: LocalDefId,
) { ) {
let expr = &body.value;
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) {
let raw_ptrs = iter_input_pats(decl, body) let raw_ptrs = iter_input_pats(decl, body)
.filter_map(|arg| raw_ptr_arg(cx, arg)) .filter_map(|arg| raw_ptr_arg(cx, arg))
.collect::<HirIdSet>(); .collect::<HirIdSet>();
if !raw_ptrs.is_empty() { if !raw_ptrs.is_empty() {
let typeck_results = cx.tcx.typeck_body(body.id()); let typeck = cx.tcx.typeck_body(body.id());
let mut v = DerefVisitor { let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
cx, match e.kind {
ptrs: raw_ptrs, hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
typeck_results, for arg in args {
}; check_arg(cx, &raw_ptrs, arg);
}
intravisit::walk_expr(&mut v, expr); },
hir::ExprKind::MethodCall(_, recv, args, _) => {
let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap();
if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe {
check_arg(cx, &raw_ptrs, recv);
for arg in args {
check_arg(cx, &raw_ptrs, arg);
}
}
},
hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr),
_ => (),
}
ControlFlow::Continue(())
});
} }
} }
} }
@ -70,54 +86,13 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId>
} }
} }
struct DerefVisitor<'a, 'tcx> { fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
cx: &'a LateContext<'tcx>, if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
ptrs: HirIdSet, span_lint(
typeck_results: &'a ty::TypeckResults<'tcx>, cx,
} NOT_UNSAFE_PTR_ARG_DEREF,
arg.span,
impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { "this public function might dereference a raw pointer but is not marked `unsafe`",
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { );
match expr.kind {
hir::ExprKind::Call(f, args) => {
let ty = self.typeck_results.expr_ty(f);
if type_is_unsafe_function(self.cx, ty) {
for arg in args {
self.check_arg(arg);
}
}
},
hir::ExprKind::MethodCall(_, receiver, args, _) => {
let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
let base_type = self.cx.tcx.type_of(def_id);
if type_is_unsafe_function(self.cx, base_type) {
self.check_arg(receiver);
for arg in args {
self.check_arg(arg);
}
}
},
hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr),
_ => (),
}
intravisit::walk_expr(self, expr);
}
}
impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
fn check_arg(&self, ptr: &hir::Expr<'_>) {
if let Some(id) = path_to_local(ptr) {
if self.ptrs.contains(&id) {
span_lint(
self.cx,
NOT_UNSAFE_PTR_ARG_DEREF,
ptr.span,
"this public function might dereference a raw pointer but is not marked `unsafe`",
);
}
}
} }
} }

View file

@ -59,10 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span,
cx, cx,
TOO_MANY_ARGUMENTS, TOO_MANY_ARGUMENTS,
fn_span, fn_span,
&format!( &format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"),
"this function has too many arguments ({}/{})",
args, too_many_arguments_threshold
),
); );
} }
} }

View file

@ -78,10 +78,7 @@ pub(super) fn check_fn(
cx, cx,
TOO_MANY_LINES, TOO_MANY_LINES,
span, span,
&format!( &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"),
"this function has too many lines ({}/{})",
line_count, too_many_lines_threshold
),
); );
} }
} }

View file

@ -1,7 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; use clippy_utils::{
contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
};
use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -76,15 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
&& let ExprKind::Block(then_block, _) = then.kind && let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr && let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
&& is_lang_ctor(cx, then_call_qpath, OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
&& is_lang_ctor(cx, qpath, OptionNone)
&& !stmts_contains_early_return(then_block.stmts) && !stmts_contains_early_return(then_block.stmts)
{ {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
format!("({})", cond_snip) format!("({cond_snip})")
} else { } else {
cond_snip.into_owned() cond_snip.into_owned()
}; };
@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
let mut method_body = if then_block.stmts.is_empty() { let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned() arg_snip.into_owned()
} else { } else {
format!("{{ /* snippet */ {} }}", arg_snip) format!("{{ /* snippet */ {arg_snip} }}")
}; };
let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
"then_some" "then_some"
@ -102,14 +102,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
}; };
let help = format!( let help = format!(
"consider using `bool::{}` like: `{}.{}({})`", "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",
method_name, cond_snip, method_name, method_body,
); );
span_lint_and_help( span_lint_and_help(
cx, cx,
IF_THEN_SOME_ELSE_NONE, IF_THEN_SOME_ELSE_NONE,
expr.span, expr.span,
&format!("this could be simplified with `bool::{}`", method_name), &format!("this could be simplified with `bool::{method_name}`"),
None, None,
&help, &help,
); );

View file

@ -5,6 +5,7 @@ use rustc_errors::Diagnostic;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -12,7 +13,6 @@ use rustc_middle::ty::{Ty, TypeckResults};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_hir_analysis::hir_ty_to_ty;
use if_chain::if_chain; use if_chain::if_chain;
@ -89,8 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
( (
generics_suggestion_span, generics_suggestion_span,
format!( format!(
"<{}{}S: ::std::hash::BuildHasher{}>", "<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
generics_snip,
if generics_snip.is_empty() { "" } else { ", " }, if generics_snip.is_empty() { "" } else { ", " },
if vis.suggestions.is_empty() { if vis.suggestions.is_empty() {
"" ""
@ -263,8 +262,8 @@ impl<'tcx> ImplicitHasherType<'tcx> {
fn type_arguments(&self) -> String { fn type_arguments(&self) -> String {
match *self { match *self {
ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"),
ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), ImplicitHasherType::HashSet(.., ref t) => format!("{t}"),
} }
} }

View file

@ -2,10 +2,11 @@ use clippy_utils::{
diagnostics::span_lint_hir_and_then, diagnostics::span_lint_hir_and_then,
get_async_fn_body, is_async_fn, get_async_fn_body, is_async_fn,
source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
visitors::expr_visitor_no_bodies, visitors::for_each_expr,
}; };
use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -53,7 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
span, span,
"missing `return` statement", "missing `return` statement",
|diag| { |diag| {
diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app); diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app);
}, },
); );
} }
@ -71,7 +72,7 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
diag.span_suggestion( diag.span_suggestion(
break_span, break_span,
"change `break` to `return` as shown", "change `break` to `return` as shown",
format!("return {}", snip), format!("return {snip}"),
app, app,
); );
}, },
@ -152,7 +153,7 @@ fn lint_implicit_returns(
ExprKind::Loop(block, ..) => { ExprKind::Loop(block, ..) => {
let mut add_return = false; let mut add_return = false;
expr_visitor_no_bodies(|e| { let _: Option<!> = for_each_expr(block, |e| {
if let ExprKind::Break(dest, sub_expr) = e.kind { if let ExprKind::Break(dest, sub_expr) = e.kind {
if dest.target_id.ok() == Some(expr.hir_id) { if dest.target_id.ok() == Some(expr.hir_id) {
if call_site_span.is_none() && e.span.ctxt() == ctxt { if call_site_span.is_none() && e.span.ctxt() == ctxt {
@ -167,9 +168,8 @@ fn lint_implicit_returns(
} }
} }
} }
true ControlFlow::Continue(())
}) });
.visit_block(block);
if add_return { if add_return {
#[expect(clippy::option_if_let_else)] #[expect(clippy::option_if_let_else)]
if let Some(span) = call_site_span { if let Some(span) = call_site_span {

View file

@ -0,0 +1,114 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{Int, IntTy, Ty, Uint, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for implicit saturating addition.
///
/// ### Why is this bad?
/// The built-in function is more readable and may be faster.
///
/// ### Example
/// ```rust
///let mut u:u32 = 7000;
///
/// if u != u32::MAX {
/// u += 1;
/// }
/// ```
/// Use instead:
/// ```rust
///let mut u:u32 = 7000;
///
/// u = u.saturating_add(1);
/// ```
#[clippy::version = "1.65.0"]
pub IMPLICIT_SATURATING_ADD,
style,
"Perform saturating addition instead of implicitly checking max bound of data type"
}
declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! {
if let ExprKind::If(cond, then, None) = expr.kind;
if let ExprKind::DropTemps(expr1) = cond.kind;
if let Some((c, op_node, l)) = get_const(cx, expr1);
if let BinOpKind::Ne | BinOpKind::Lt = op_node;
if let ExprKind::Block(block, None) = then.kind;
if let Block {
stmts:
[Stmt
{ kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }],
expr: None, ..} |
Block { stmts: [], expr: Some(ex), ..} = block;
if let ExprKind::AssignOp(op1, target, value) = ex.kind;
let ty = cx.typeck_results().expr_ty(target);
if Some(c) == get_int_max(ty);
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
if BinOpKind::Add == op1.node;
if let ExprKind::Lit(ref lit) = value.kind;
if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
if block.expr.is_none();
then {
let mut app = Applicability::MachineApplicable;
let code = snippet_with_applicability(cx, target.span, "_", &mut app);
let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")};
span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
}
}
}
}
fn get_int_max(ty: Ty<'_>) -> Option<u128> {
match ty.peel_refs().kind() {
Int(IntTy::I8) => i8::max_value().try_into().ok(),
Int(IntTy::I16) => i16::max_value().try_into().ok(),
Int(IntTy::I32) => i32::max_value().try_into().ok(),
Int(IntTy::I64) => i64::max_value().try_into().ok(),
Int(IntTy::I128) => i128::max_value().try_into().ok(),
Int(IntTy::Isize) => isize::max_value().try_into().ok(),
Uint(UintTy::U8) => u8::max_value().try_into().ok(),
Uint(UintTy::U16) => u16::max_value().try_into().ok(),
Uint(UintTy::U32) => u32::max_value().try_into().ok(),
Uint(UintTy::U64) => u64::max_value().try_into().ok(),
Uint(UintTy::U128) => Some(u128::max_value()),
Uint(UintTy::Usize) => usize::max_value().try_into().ok(),
_ => None,
}
}
fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> {
if let ExprKind::Binary(op, l, r) = expr.kind {
let tr = cx.typeck_results();
if let Some((Constant::Int(c), _)) = constant(cx, tr, r) {
return Some((c, op.node, l));
};
if let Some((Constant::Int(c), _)) = constant(cx, tr, l) {
return Some((c, invert_op(op.node)?, r));
}
}
None
}
fn invert_op(op: BinOpKind) -> Option<BinOpKind> {
use rustc_hir::BinOpKind::{Ge, Gt, Le, Lt, Ne};
match op {
Lt => Some(Gt),
Le => Some(Ge),
Ne => Some(Ne),
Ge => Some(Le),
Gt => Some(Lt),
_ => None,
}
}

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -131,17 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
match peel_blocks_with_stmt(expr).kind { match peel_blocks_with_stmt(expr).kind {
ExprKind::AssignOp(ref op1, target, value) => { ExprKind::AssignOp(ref op1, target, value) => {
if_chain! { // Check if literal being subtracted is one
if BinOpKind::Sub == op1.node; (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
// Check if literal being subtracted is one
if let ExprKind::Lit(ref lit1) = value.kind;
if let LitKind::Int(1, _) = lit1.node;
then {
Some(target)
} else {
None
}
}
}, },
ExprKind::Assign(target, value, _) => { ExprKind::Assign(target, value, _) => {
if_chain! { if_chain! {
@ -150,8 +141,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
if SpanlessEq::new(cx).eq_expr(left1, target); if SpanlessEq::new(cx).eq_expr(left1, target);
if let ExprKind::Lit(ref lit1) = right1.kind; if is_integer_literal(right1, 1);
if let LitKind::Int(1, _) = lit1.node;
then { then {
Some(target) Some(target)
} else { } else {
@ -170,7 +160,7 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
expr.span, expr.span,
"implicitly performing saturating subtraction", "implicitly performing saturating subtraction",
"try", "try",
format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), format!("{var_name} = {var_name}.saturating_sub({});", '1'),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
let mut fields_snippet = String::new(); let mut fields_snippet = String::new();
let (last_ident, idents) = ordered_fields.split_last().unwrap(); let (last_ident, idents) = ordered_fields.split_last().unwrap();
for ident in idents { for ident in idents {
let _ = write!(fields_snippet, "{}, ", ident); let _ = write!(fields_snippet, "{ident}, ");
} }
fields_snippet.push_str(&last_ident.to_string()); fields_snippet.push_str(&last_ident.to_string());
@ -100,10 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
String::new() String::new()
}; };
let sugg = format!("{} {{ {}{} }}", let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}",
snippet(cx, qpath.span(), ".."), snippet(cx, qpath.span(), ".."),
fields_snippet,
base_snippet,
); );
span_lint_and_sugg( span_lint_and_sugg(

View file

@ -139,14 +139,14 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
.map(|(index, _)| *index) .map(|(index, _)| *index)
.collect::<FxHashSet<_>>(); .collect::<FxHashSet<_>>();
let value_name = |index| format!("{}_{}", slice.ident.name, index); let value_name = |index| format!("{}_{index}", slice.ident.name);
if let Some(max_index) = used_indices.iter().max() { if let Some(max_index) = used_indices.iter().max() {
let opt_ref = if slice.needs_ref { "ref " } else { "" }; let opt_ref = if slice.needs_ref { "ref " } else { "" };
let pat_sugg_idents = (0..=*max_index) let pat_sugg_idents = (0..=*max_index)
.map(|index| { .map(|index| {
if used_indices.contains(&index) { if used_indices.contains(&index) {
format!("{}{}", opt_ref, value_name(index)) format!("{opt_ref}{}", value_name(index))
} else { } else {
"_".to_string() "_".to_string()
} }

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::higher;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{higher, match_def_path, path_def_id, paths};
use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -168,9 +168,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
}, },
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
ExprKind::Call(path, _) => path_def_id(cx, path) ExprKind::Call(path, _) => {
.map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT)) if let ExprKind::Path(ref qpath) = path.kind {
.into(), cx.qpath_res(qpath, path.hir_id)
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
.into()
} else {
Finite
}
},
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite, _ => Finite,
} }

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; use clippy_utils::{return_ty, trait_ref_of_method};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
} }
fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); let display_trait_id = cx
.tcx
.get_diagnostic_item(sym::Display)
.expect("Failed to get trait ID of `Display`!");
// Get the real type of 'self' // Get the real type of 'self'
let self_type = cx.tcx.fn_sig(item.def_id).input(0); let self_type = cx.tcx.fn_sig(item.def_id).input(0);
@ -131,23 +134,19 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
INHERENT_TO_STRING_SHADOW_DISPLAY, INHERENT_TO_STRING_SHADOW_DISPLAY,
item.span, item.span,
&format!( &format!(
"type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", "type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`"
self_type
), ),
None, None,
&format!("remove the inherent method from type `{}`", self_type), &format!("remove the inherent method from type `{self_type}`"),
); );
} else { } else {
span_lint_and_help( span_lint_and_help(
cx, cx,
INHERENT_TO_STRING, INHERENT_TO_STRING,
item.span, item.span,
&format!( &format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"),
"implementation of inherent method `to_string(&self) -> String` for type `{}`",
self_type
),
None, None,
&format!("implement trait `Display` for type `{}` instead", self_type), &format!("implement trait `Display` for type `{self_type}` instead"),
); );
} }
} }

View file

@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
cx, cx,
INLINE_FN_WITHOUT_BODY, INLINE_FN_WITHOUT_BODY,
attr.span, attr.span,
&format!("use of `#[inline]` on trait method `{}` which has no body", name), &format!("use of `#[inline]` on trait method `{name}` which has no body"),
|diag| { |diag| {
diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
}, },

View file

@ -138,8 +138,8 @@ impl IntPlusOne {
if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(snippet) = snippet_opt(cx, node.span) {
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
let rec = match side { let rec = match side {
Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
}; };
return rec; return rec;
} }

View file

@ -80,10 +80,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI
cx, cx,
ITER_NOT_RETURNING_ITERATOR, ITER_NOT_RETURNING_ITERATOR,
sig.span, sig.span,
&format!( &format!("this method is named `{name}` but its return type does not implement `Iterator`"),
"this method is named `{}` but its return type does not implement `Iterator`",
name
),
); );
} }
} }

View file

@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind}; use rustc_middle::ty::{self, ConstKind};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span};
use rustc_hir_analysis::hir_ty_to_ty;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does

View file

@ -210,7 +210,8 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
} }
} }
if cx.access_levels.is_exported(visited_trait.def_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) if cx.access_levels.is_exported(visited_trait.def_id.def_id)
&& trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
{ {
let mut current_and_super_traits = DefIdSet::default(); let mut current_and_super_traits = DefIdSet::default();
fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
@ -278,15 +279,13 @@ impl<'tcx> LenOutput<'tcx> {
_ => "", _ => "",
}; };
match self { match self {
Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"),
Self::Option(_) => format!( Self::Option(_) => {
"expected signature: `({}self) -> bool` or `({}self) -> Option<bool>", format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option<bool>")
self_ref, self_ref },
), Self::Result(..) => {
Self::Result(..) => format!( format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result<bool>")
"expected signature: `({}self) -> bool` or `({}self) -> Result<bool>", },
self_ref, self_ref
),
} }
} }
} }
@ -326,8 +325,7 @@ fn check_for_is_empty<'tcx>(
let (msg, is_empty_span, self_kind) = match is_empty { let (msg, is_empty_span, self_kind) = match is_empty {
None => ( None => (
format!( format!(
"{} `{}` has a public `len` method, but no `is_empty` method", "{item_kind} `{}` has a public `len` method, but no `is_empty` method",
item_kind,
item_name.as_str(), item_name.as_str(),
), ),
None, None,
@ -335,8 +333,7 @@ fn check_for_is_empty<'tcx>(
), ),
Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => (
format!( format!(
"{} `{}` has a public `len` method, but a private `is_empty` method", "{item_kind} `{}` has a public `len` method, but a private `is_empty` method",
item_kind,
item_name.as_str(), item_name.as_str(),
), ),
Some(cx.tcx.def_span(is_empty.def_id)), Some(cx.tcx.def_span(is_empty.def_id)),
@ -348,8 +345,7 @@ fn check_for_is_empty<'tcx>(
{ {
( (
format!( format!(
"{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
item_kind,
item_name.as_str(), item_name.as_str(),
), ),
Some(cx.tcx.def_span(is_empty.def_id)), Some(cx.tcx.def_span(is_empty.def_id)),
@ -419,10 +415,9 @@ fn check_len(
LEN_ZERO, LEN_ZERO,
span, span,
&format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
&format!("using `{}is_empty` is clearer and more explicit", op), &format!("using `{op}is_empty` is clearer and more explicit"),
format!( format!(
"{}{}.is_empty()", "{op}{}.is_empty()",
op,
snippet_with_applicability(cx, receiver.span, "_", &mut applicability) snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
), ),
applicability, applicability,
@ -439,10 +434,9 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
COMPARISON_TO_EMPTY, COMPARISON_TO_EMPTY,
span, span,
"comparison to empty slice", "comparison to empty slice",
&format!("using `{}is_empty` is clearer and more explicit", op), &format!("using `{op}is_empty` is clearer and more explicit"),
format!( format!(
"{}{}.is_empty()", "{op}{}.is_empty()",
op,
snippet_with_applicability(cx, lit1.span, "_", &mut applicability) snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
), ),
applicability, applicability,

View file

@ -106,8 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
// use mutably after the `if` // use mutably after the `if`
let sug = format!( let sug = format!(
"let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
mut=mutability,
name=ident.name, name=ident.name,
cond=snippet(cx, cond.span, "_"), cond=snippet(cx, cond.span, "_"),
then=if then.stmts.len() > 1 { " ..;" } else { "" }, then=if then.stmts.len() > 1 { " ..;" } else { "" },

View file

@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
LintId::of(box_default::BOX_DEFAULT),
LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_ENUM_TRUNCATION),
@ -44,7 +45,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_macros::DISALLOWED_MACROS),
LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_methods::DISALLOWED_METHODS),
LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_names::DISALLOWED_NAMES),
LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(disallowed_types::DISALLOWED_TYPES),
@ -85,6 +86,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(functions::TOO_MANY_ARGUMENTS),
LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER), LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(inherent_to_string::INHERENT_TO_STRING),
@ -125,6 +127,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(main_recursion::MAIN_RECURSION), LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_clamp::MANUAL_CLAMP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_retain::MANUAL_RETAIN), LintId::of(manual_retain::MANUAL_RETAIN),

View file

@ -22,6 +22,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::SINGLE_ELEMENT_LOOP),
LintId::of(loops::WHILE_LET_LOOP), LintId::of(loops::WHILE_LET_LOOP),
LintId::of(manual_clamp::MANUAL_CLAMP),
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_strip::MANUAL_STRIP), LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),

View file

@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::INVALID_PATHS), LintId::of(utils::internal_lints::INVALID_PATHS),
LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL),
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
LintId::of(utils::internal_lints::PRODUCE_ICE), LintId::of(utils::internal_lints::PRODUCE_ICE),
LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH),
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
]) ])

View file

@ -24,8 +24,6 @@ store.register_lints(&[
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::LINT_WITHOUT_LINT_PASS, utils::internal_lints::LINT_WITHOUT_LINT_PASS,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
#[cfg(feature = "internal")]
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::MISSING_MSRV_ATTR_IMPL, utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
@ -34,6 +32,8 @@ store.register_lints(&[
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::PRODUCE_ICE, utils::internal_lints::PRODUCE_ICE,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::UNNECESSARY_DEF_PATH,
#[cfg(feature = "internal")]
utils::internal_lints::UNNECESSARY_SYMBOL_STR, utils::internal_lints::UNNECESSARY_SYMBOL_STR,
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT, approx_const::APPROX_CONSTANT,
@ -60,6 +60,7 @@ store.register_lints(&[
booleans::NONMINIMAL_BOOL, booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR, booleans::OVERLY_COMPLEX_BOOL_EXPR,
borrow_deref_ref::BORROW_DEREF_REF, borrow_deref_ref::BORROW_DEREF_REF,
box_default::BOX_DEFAULT,
cargo::CARGO_COMMON_METADATA, cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS, cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES, cargo::NEGATIVE_FEATURE_NAMES,
@ -113,16 +114,17 @@ store.register_lints(&[
derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
derive::EXPL_IMPL_CLONE_ON_COPY, derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE, derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_macros::DISALLOWED_MACROS,
disallowed_methods::DISALLOWED_METHODS, disallowed_methods::DISALLOWED_METHODS,
disallowed_names::DISALLOWED_NAMES, disallowed_names::DISALLOWED_NAMES,
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
disallowed_types::DISALLOWED_TYPES, disallowed_types::DISALLOWED_TYPES,
doc::DOC_LINK_WITH_QUOTES,
doc::DOC_MARKDOWN, doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC, doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC, doc::MISSING_PANICS_DOC,
doc::MISSING_SAFETY_DOC, doc::MISSING_SAFETY_DOC,
doc::NEEDLESS_DOCTEST_MAIN, doc::NEEDLESS_DOCTEST_MAIN,
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
double_parens::DOUBLE_PARENS, double_parens::DOUBLE_PARENS,
drop_forget_ref::DROP_COPY, drop_forget_ref::DROP_COPY,
drop_forget_ref::DROP_NON_DROP, drop_forget_ref::DROP_NON_DROP,
@ -159,6 +161,7 @@ store.register_lints(&[
format::USELESS_FORMAT, format::USELESS_FORMAT,
format_args::FORMAT_IN_FORMAT_ARGS, format_args::FORMAT_IN_FORMAT_ARGS,
format_args::TO_STRING_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS,
format_args::UNINLINED_FORMAT_ARGS,
format_impl::PRINT_IN_FORMAT_IMPL, format_impl::PRINT_IN_FORMAT_IMPL,
format_impl::RECURSIVE_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL,
format_push_string::FORMAT_PUSH_STRING, format_push_string::FORMAT_PUSH_STRING,
@ -182,6 +185,7 @@ store.register_lints(&[
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
implicit_hasher::IMPLICIT_HASHER, implicit_hasher::IMPLICIT_HASHER,
implicit_return::IMPLICIT_RETURN, implicit_return::IMPLICIT_RETURN,
implicit_saturating_add::IMPLICIT_SATURATING_ADD,
implicit_saturating_sub::IMPLICIT_SATURATING_SUB, implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
index_refutable_slice::INDEX_REFUTABLE_SLICE, index_refutable_slice::INDEX_REFUTABLE_SLICE,
@ -243,6 +247,7 @@ store.register_lints(&[
manual_assert::MANUAL_ASSERT, manual_assert::MANUAL_ASSERT,
manual_async_fn::MANUAL_ASYNC_FN, manual_async_fn::MANUAL_ASYNC_FN,
manual_bits::MANUAL_BITS, manual_bits::MANUAL_BITS,
manual_clamp::MANUAL_CLAMP,
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
manual_rem_euclid::MANUAL_REM_EUCLID, manual_rem_euclid::MANUAL_REM_EUCLID,

View file

@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(copies::BRANCHES_SHARING_CODE),
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(equatable_if_let::EQUATABLE_IF_LET),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),

View file

@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(dereference::REF_BINDING_TO_REFERENCE), LintId::of(dereference::REF_BINDING_TO_REFERENCE),
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
LintId::of(doc::DOC_LINK_WITH_QUOTES),
LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::DOC_MARKDOWN),
LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_ERRORS_DOC),
LintId::of(doc::MISSING_PANICS_DOC), LintId::of(doc::MISSING_PANICS_DOC),
LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
LintId::of(empty_enum::EMPTY_ENUM), LintId::of(empty_enum::EMPTY_ENUM),
LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
LintId::of(format_args::UNINLINED_FORMAT_ARGS),
LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::MUST_USE_CANDIDATE),
LintId::of(functions::TOO_MANY_LINES), LintId::of(functions::TOO_MANY_LINES),
LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(if_not_else::IF_NOT_ELSE),

View file

@ -3,6 +3,7 @@
// Manual edits will be overwritten. // Manual edits will be overwritten.
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(box_default::BOX_DEFAULT),
LintId::of(entry::MAP_ENTRY), LintId::of(entry::MAP_ENTRY),
LintId::of(escape::BOXED_LOCAL), LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),

View file

@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
LintId::of(dereference::NEEDLESS_BORROW), LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_macros::DISALLOWED_MACROS),
LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_methods::DISALLOWED_METHODS),
LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_names::DISALLOWED_NAMES),
LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(disallowed_types::DISALLOWED_TYPES),
@ -30,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY), LintId::of(len_zero::COMPARISON_TO_EMPTY),

View file

@ -31,6 +31,7 @@ extern crate rustc_data_structures;
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_errors; extern crate rustc_errors;
extern crate rustc_hir; extern crate rustc_hir;
extern crate rustc_hir_analysis;
extern crate rustc_hir_pretty; extern crate rustc_hir_pretty;
extern crate rustc_index; extern crate rustc_index;
extern crate rustc_infer; extern crate rustc_infer;
@ -43,7 +44,6 @@ extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate rustc_target; extern crate rustc_target;
extern crate rustc_trait_selection; extern crate rustc_trait_selection;
extern crate rustc_hir_analysis;
#[macro_use] #[macro_use]
extern crate clippy_utils; extern crate clippy_utils;
@ -180,6 +180,7 @@ mod bool_assert_comparison;
mod bool_to_int_with_if; mod bool_to_int_with_if;
mod booleans; mod booleans;
mod borrow_deref_ref; mod borrow_deref_ref;
mod box_default;
mod cargo; mod cargo;
mod casts; mod casts;
mod checked_conversions; mod checked_conversions;
@ -198,12 +199,12 @@ mod default_union_representation;
mod dereference; mod dereference;
mod derivable_impls; mod derivable_impls;
mod derive; mod derive;
mod disallowed_macros;
mod disallowed_methods; mod disallowed_methods;
mod disallowed_names; mod disallowed_names;
mod disallowed_script_idents; mod disallowed_script_idents;
mod disallowed_types; mod disallowed_types;
mod doc; mod doc;
mod doc_link_with_quotes;
mod double_parens; mod double_parens;
mod drop_forget_ref; mod drop_forget_ref;
mod duplicate_mod; mod duplicate_mod;
@ -238,6 +239,7 @@ mod if_not_else;
mod if_then_some_else_none; mod if_then_some_else_none;
mod implicit_hasher; mod implicit_hasher;
mod implicit_return; mod implicit_return;
mod implicit_saturating_add;
mod implicit_saturating_sub; mod implicit_saturating_sub;
mod inconsistent_struct_constructor; mod inconsistent_struct_constructor;
mod index_refutable_slice; mod index_refutable_slice;
@ -267,6 +269,7 @@ mod main_recursion;
mod manual_assert; mod manual_assert;
mod manual_async_fn; mod manual_async_fn;
mod manual_bits; mod manual_bits;
mod manual_clamp;
mod manual_instant_elapsed; mod manual_instant_elapsed;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_rem_euclid; mod manual_rem_euclid;
@ -416,8 +419,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
let msrv = conf.msrv.as_ref().and_then(|s| { let msrv = conf.msrv.as_ref().and_then(|s| {
parse_msrv(s, None, None).or_else(|| { parse_msrv(s, None, None).or_else(|| {
sess.err(&format!( sess.err(&format!(
"error reading Clippy's configuration file. `{}` is not a valid Rust version", "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
s
)); ));
None None
}) })
@ -433,8 +435,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
let clippy_msrv = conf.msrv.as_ref().and_then(|s| { let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
parse_msrv(s, None, None).or_else(|| { parse_msrv(s, None, None).or_else(|| {
sess.err(&format!( sess.err(&format!(
"error reading Clippy's configuration file. `{}` is not a valid Rust version", "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
s
)); ));
None None
}) })
@ -445,8 +446,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
// if both files have an msrv, let's compare them and emit a warning if they differ // if both files have an msrv, let's compare them and emit a warning if they differ
if clippy_msrv != cargo_msrv { if clippy_msrv != cargo_msrv {
sess.warn(&format!( sess.warn(&format!(
"the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`", "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
clippy_msrv
)); ));
} }
@ -465,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf {
Ok(Some(path)) => path, Ok(Some(path)) => path,
Ok(None) => return Conf::default(), Ok(None) => return Conf::default(),
Err(error) => { Err(error) => {
sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) sess.struct_err(&format!("error finding Clippy's configuration file: {error}"))
.emit(); .emit();
return Conf::default(); return Conf::default();
}, },
@ -535,9 +535,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle));
store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths));
store.register_late_pass(|_| Box::new(utils::internal_lints::InterningDefinedSymbol::default())); store.register_late_pass(|_| Box::<utils::internal_lints::InterningDefinedSymbol>::default());
store.register_late_pass(|_| Box::new(utils::internal_lints::LintWithoutLintPass::default())); store.register_late_pass(|_| Box::<utils::internal_lints::LintWithoutLintPass>::default());
store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath));
store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass));
store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
} }
@ -629,10 +629,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
msrv, msrv,
)) ))
}); });
store.register_late_pass(|_| Box::new(shadow::Shadow::default())); store.register_late_pass(|_| Box::<shadow::Shadow>::default());
store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
store.register_late_pass(|_| Box::new(loops::Loops)); store.register_late_pass(|_| Box::new(loops::Loops));
store.register_late_pass(|_| Box::new(main_recursion::MainRecursion::default())); store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(entry::HashMapPass));
store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
@ -666,7 +666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(format::UselessFormat));
store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(swap::Swap));
store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
store.register_late_pass(|_| Box::new(new_without_default::NewWithoutDefault::default())); store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>(); let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
let too_many_arguments_threshold = conf.too_many_arguments_threshold; let too_many_arguments_threshold = conf.too_many_arguments_threshold;
@ -705,7 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
store.register_late_pass(|_| Box::new(useless_conversion::UselessConversion::default())); store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
store.register_late_pass(|_| Box::new(question_mark::QuestionMark)); store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
@ -775,7 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
upper_case_acronyms_aggressive, upper_case_acronyms_aggressive,
)) ))
}); });
store.register_late_pass(|_| Box::new(default::Default::default())); store.register_late_pass(|_| Box::<default::Default>::default());
store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(exit::Exit));
@ -798,7 +798,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
store.register_late_pass(|_| Box::new(redundant_pub_crate::RedundantPubCrate::default())); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
@ -816,11 +816,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}); });
let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>(); let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher))); store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
store.register_late_pass(|_| Box::new(macro_use::MacroUseImports::default())); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_macros = conf.disallowed_macros.clone();
store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone())));
let disallowed_methods = conf.disallowed_methods.clone(); let disallowed_methods = conf.disallowed_methods.clone();
store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
@ -829,7 +831,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(strings::StrToString)); store.register_late_pass(|_| Box::new(strings::StrToString));
store.register_late_pass(|_| Box::new(strings::StringToString)); store.register_late_pass(|_| Box::new(strings::StringToString));
store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues));
store.register_late_pass(|_| Box::new(vec_init_then_push::VecInitThenPush::default())); store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
@ -857,7 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
store.register_late_pass(move |_| Box::new(format_args::FormatArgs)); store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
@ -866,8 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
store.register_late_pass(|_| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
let allow_dbg_in_tests = conf.allow_dbg_in_tests; let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish; let cargo_ignore_publish = conf.cargo_ignore_publish;
@ -876,7 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
ignore_publish: cargo_ignore_publish, ignore_publish: cargo_ignore_publish,
}) })
}); });
store.register_late_pass(|_| Box::new(write::Write::default())); store.register_late_pass(|_| Box::<write::Write>::default());
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
@ -886,7 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
@ -898,13 +899,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
store.register_late_pass(|_| Box::new(std_instead_of_core::StdReexports::default())); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
store.register_late_pass(|_| Box::new(box_default::BoxDefault));
store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -9,8 +9,8 @@ use rustc_hir::intravisit::{
use rustc_hir::FnRetTy::Return; use rustc_hir::FnRetTy::Return;
use rustc_hir::{ use rustc_hir::{
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn,
TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::hir::nested_filter as middle_nested_filter;
@ -276,7 +276,7 @@ fn could_use_elision<'tcx>(
let mut checker = BodyLifetimeChecker { let mut checker = BodyLifetimeChecker {
lifetimes_used_in_body: false, lifetimes_used_in_body: false,
}; };
checker.visit_expr(&body.value); checker.visit_expr(body.value);
if checker.lifetimes_used_in_body { if checker.lifetimes_used_in_body {
return false; return false;
} }

View file

@ -478,7 +478,7 @@ impl DecimalLiteralRepresentation {
if num_lit.radix == Radix::Decimal; if num_lit.radix == Radix::Decimal;
if val >= u128::from(self.threshold); if val >= u128::from(self.threshold);
then { then {
let hex = format!("{:#X}", val); let hex = format!("{val:#X}");
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
warning_type.display(num_lit.format(), cx, lit.span); warning_type.display(num_lit.format(), cx, lit.span);

View file

@ -44,11 +44,10 @@ pub(super) fn check<'tcx>(
cx, cx,
EXPLICIT_COUNTER_LOOP, EXPLICIT_COUNTER_LOOP,
span, span,
&format!("the variable `{}` is used as a loop counter", name), &format!("the variable `{name}` is used as a loop counter"),
"consider using", "consider using",
format!( format!(
"for ({}, {}) in {}.enumerate()", "for ({name}, {}) in {}.enumerate()",
name,
snippet_with_applicability(cx, pat.span, "item", &mut applicability), snippet_with_applicability(cx, pat.span, "item", &mut applicability),
make_iterator_snippet(cx, arg, &mut applicability), make_iterator_snippet(cx, arg, &mut applicability),
), ),
@ -65,24 +64,21 @@ pub(super) fn check<'tcx>(
cx, cx,
EXPLICIT_COUNTER_LOOP, EXPLICIT_COUNTER_LOOP,
span, span,
&format!("the variable `{}` is used as a loop counter", name), &format!("the variable `{name}` is used as a loop counter"),
|diag| { |diag| {
diag.span_suggestion( diag.span_suggestion(
span, span,
"consider using", "consider using",
format!( format!(
"for ({}, {}) in (0_{}..).zip({})", "for ({name}, {}) in (0_{int_name}..).zip({})",
name,
snippet_with_applicability(cx, pat.span, "item", &mut applicability), snippet_with_applicability(cx, pat.span, "item", &mut applicability),
int_name,
make_iterator_snippet(cx, arg, &mut applicability), make_iterator_snippet(cx, arg, &mut applicability),
), ),
applicability, applicability,
); );
diag.note(&format!( diag.note(&format!(
"`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`", "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
name, int_name
)); ));
}, },
); );

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