mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 15:11:30 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
e2808afd60
621 changed files with 10302 additions and 4445 deletions
159
CHANGELOG.md
159
CHANGELOG.md
|
@ -6,11 +6,161 @@ document.
|
|||
|
||||
## 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
|
||||
|
||||
Current stable, released 2022-08-11
|
||||
Released 2022-08-11
|
||||
|
||||
[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
|
||||
[`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_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
|
||||
[`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
|
||||
|
@ -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_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
|
||||
[`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_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
|
||||
|
@ -3766,6 +3918,7 @@ Released 2018-09-13
|
|||
[`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_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
|
||||
[`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
|
||||
|
@ -3834,6 +3987,7 @@ Released 2018-09-13
|
|||
[`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_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_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
|
||||
|
@ -4124,6 +4278,7 @@ Released 2018-09-13
|
|||
[`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_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_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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.65"
|
||||
version = "0.1.66"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -23,12 +23,12 @@ path = "src/driver.rs"
|
|||
[dependencies]
|
||||
clippy_lints = { path = "clippy_lints" }
|
||||
semver = "1.0"
|
||||
rustc_tools_util = { path = "rustc_tools_util" }
|
||||
rustc_tools_util = "0.2.1"
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
termize = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = { version = "0.8", features = ["tmp"] }
|
||||
compiletest_rs = { version = "0.9", features = ["tmp"] }
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
toml = "0.5"
|
||||
|
@ -42,6 +42,7 @@ filetime = "0.2"
|
|||
rustc-workspace-hack = "1.0"
|
||||
|
||||
# UI test dependencies
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
|
@ -55,7 +56,7 @@ tokio = { version = "1", features = ["io-util"] }
|
|||
rustc-semver = "1.1"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
|
||||
rustc_tools_util = "0.2.1"
|
||||
|
||||
[features]
|
||||
deny-warnings = ["clippy_lints/deny-warnings"]
|
||||
|
|
46
README.md
46
README.md
|
@ -139,25 +139,6 @@ line. (You can swap `clippy::all` with the specific lint category you are target
|
|||
|
||||
## 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
|
||||
|
||||
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::...
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
|
||||
|
|
|
@ -82,16 +82,16 @@ pub fn run(check: bool, verbose: bool) {
|
|||
fn output_err(err: CliError) {
|
||||
match err {
|
||||
CliError::CommandFailed(command, stderr) => {
|
||||
eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr);
|
||||
eprintln!("error: A command failed! `{command}`\nstderr: {stderr}");
|
||||
},
|
||||
CliError::IoError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
eprintln!("error: {err}");
|
||||
},
|
||||
CliError::RustfmtNotInstalled => {
|
||||
eprintln!("error: rustfmt nightly is not installed.");
|
||||
},
|
||||
CliError::WalkDirError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
eprintln!("error: {err}");
|
||||
},
|
||||
CliError::IntellijSetupActive => {
|
||||
eprintln!(
|
||||
|
|
|
@ -41,7 +41,7 @@ fn main() {
|
|||
matches.contains_id("msrv"),
|
||||
) {
|
||||
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() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::clippy_project_root;
|
||||
use indoc::{indoc, writedoc};
|
||||
use indoc::{formatdoc, writedoc};
|
||||
use std::fmt::Write as _;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::prelude::*;
|
||||
|
@ -23,7 +23,7 @@ impl<T> Context for io::Result<T> {
|
|||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(e) => {
|
||||
let message = format!("{}: {}", text.as_ref(), e);
|
||||
let message = format!("{}: {e}", text.as_ref());
|
||||
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_path = format!("clippy_lints/src/{}.rs", lint.name);
|
||||
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(())
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
|||
|
||||
path.push("src");
|
||||
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)))?;
|
||||
|
||||
Ok(())
|
||||
|
@ -106,7 +106,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
|||
let test_contents = get_test_file_contents(lint.name, None);
|
||||
write_file(lint.project_root.join(&test_path), test_contents)?;
|
||||
|
||||
println!("Generated test file: `{}`", test_path);
|
||||
println!("Generated test file: `{test_path}`");
|
||||
}
|
||||
|
||||
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 {
|
||||
let mut contents = format!(
|
||||
indoc! {"
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::{})]
|
||||
let mut contents = formatdoc!(
|
||||
r#"
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::{lint_name})]
|
||||
|
||||
fn main() {{
|
||||
// test code goes here
|
||||
}}
|
||||
"},
|
||||
lint_name
|
||||
fn main() {{
|
||||
// test code goes here
|
||||
}}
|
||||
"#
|
||||
);
|
||||
|
||||
if let Some(header) = header_commands {
|
||||
contents = format!("{}\n{}", header, contents);
|
||||
contents = format!("{header}\n{contents}");
|
||||
}
|
||||
|
||||
contents
|
||||
}
|
||||
|
||||
fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
|
||||
format!(
|
||||
indoc! {r#"
|
||||
# {}
|
||||
formatdoc!(
|
||||
r#"
|
||||
# {hint}
|
||||
|
||||
[package]
|
||||
name = "{}"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
[package]
|
||||
name = "{lint_name}"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
"#},
|
||||
hint, lint_name
|
||||
[workspace]
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -238,76 +236,61 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
let name_upper = lint_name.to_uppercase();
|
||||
|
||||
result.push_str(&if enable_msrv {
|
||||
format!(
|
||||
indoc! {"
|
||||
use clippy_utils::msrvs;
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
|
||||
formatdoc!(
|
||||
r#"
|
||||
use clippy_utils::msrvs;
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
|
||||
|
||||
"},
|
||||
pass_type = pass_type,
|
||||
pass_import = pass_import,
|
||||
context_import = context_import,
|
||||
"#
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
indoc! {"
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}}};
|
||||
use rustc_session::{{declare_lint_pass, declare_tool_lint}};
|
||||
formatdoc!(
|
||||
r#"
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}}};
|
||||
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));
|
||||
|
||||
result.push_str(&if enable_msrv {
|
||||
format!(
|
||||
indoc! {"
|
||||
pub struct {name_camel} {{
|
||||
msrv: Option<RustcVersion>,
|
||||
formatdoc!(
|
||||
r#"
|
||||
pub struct {name_camel} {{
|
||||
msrv: Option<RustcVersion>,
|
||||
}}
|
||||
|
||||
impl {name_camel} {{
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {{
|
||||
Self {{ msrv }}
|
||||
}}
|
||||
}}
|
||||
|
||||
impl {name_camel} {{
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {{
|
||||
Self {{ msrv }}
|
||||
}}
|
||||
}}
|
||||
impl_lint_pass!({name_camel} => [{name_upper}]);
|
||||
|
||||
impl_lint_pass!({name_camel} => [{name_upper}]);
|
||||
impl {pass_type}{pass_lifetimes} for {name_camel} {{
|
||||
extract_msrv_attr!({context_import});
|
||||
}}
|
||||
|
||||
impl {pass_type}{pass_lifetimes} for {name_camel} {{
|
||||
extract_msrv_attr!({context_import});
|
||||
}}
|
||||
|
||||
// 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,
|
||||
// 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`
|
||||
"#
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
indoc! {"
|
||||
declare_lint_pass!({name_camel} => [{name_upper}]);
|
||||
formatdoc!(
|
||||
r#"
|
||||
declare_lint_pass!({name_camel} => [{name_upper}]);
|
||||
|
||||
impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
|
||||
"},
|
||||
pass_type = pass_type,
|
||||
pass_lifetimes = pass_lifetimes,
|
||||
name_upper = name_upper,
|
||||
name_camel = name_camel,
|
||||
impl {pass_type}{pass_lifetimes} for {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 {
|
||||
format!(
|
||||
indoc! {r#"
|
||||
formatdoc!(
|
||||
r#"
|
||||
declare_clippy_lint! {{
|
||||
/// ### What it does
|
||||
///
|
||||
|
@ -330,15 +313,13 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String {
|
|||
/// ```rust
|
||||
/// // example code which does not raise clippy warning
|
||||
/// ```
|
||||
#[clippy::version = "{version}"]
|
||||
#[clippy::version = "{}"]
|
||||
pub {name_upper},
|
||||
{category},
|
||||
"default lint description"
|
||||
}}
|
||||
"#},
|
||||
version = get_stabilization_version(),
|
||||
name_upper = name_upper,
|
||||
category = category,
|
||||
"#,
|
||||
get_stabilization_version(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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!(
|
||||
ty_dir.exists() && ty_dir.is_dir(),
|
||||
"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)?;
|
||||
println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name);
|
||||
println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name);
|
||||
println!(
|
||||
"Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!",
|
||||
lint.name, ty
|
||||
"Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!",
|
||||
lint.name
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -542,7 +523,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
.chain(std::iter::once(&*lint_name_upper))
|
||||
.filter(|s| !s.is_empty())
|
||||
{
|
||||
let _ = write!(new_arr_content, "\n {},", ident);
|
||||
let _ = write!(new_arr_content, "\n {ident},");
|
||||
}
|
||||
new_arr_content.push('\n');
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ use std::time::{Duration, SystemTime};
|
|||
/// Panics if the python commands could not be spawned
|
||||
pub fn run(port: u16, lint: Option<&String>) -> ! {
|
||||
let mut url = Some(match lint {
|
||||
None => format!("http://localhost:{}", port),
|
||||
Some(lint) => format!("http://localhost:{}/#{}", port, lint),
|
||||
None => format!("http://localhost:{port}"),
|
||||
Some(lint) => format!("http://localhost:{port}/#{lint}"),
|
||||
});
|
||||
|
||||
loop {
|
||||
|
|
|
@ -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!("git hook successfully installed");
|
||||
},
|
||||
Err(err) => eprintln!(
|
||||
"error: unable to copy `{}` to `{}` ({})",
|
||||
HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
|
||||
),
|
||||
Err(err) => eprintln!("error: unable to copy `{HOOK_SOURCE_FILE}` to `{HOOK_TARGET_FILE}` ({err})"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +74,7 @@ pub fn remove_hook() {
|
|||
|
||||
fn delete_git_hook_file(path: &Path) -> bool {
|
||||
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
|
||||
} else {
|
||||
true
|
||||
|
|
|
@ -60,7 +60,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
|
|||
path = absolute_path;
|
||||
},
|
||||
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(());
|
||||
},
|
||||
};
|
||||
|
@ -103,14 +103,14 @@ fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo
|
|||
fn read_project_file(file_path: &str) -> Result<String, ()> {
|
||||
let path = Path::new(file_path);
|
||||
if !path.exists() {
|
||||
eprintln!("error: unable to find the file `{}`", file_path);
|
||||
eprintln!("error: unable to find the file `{file_path}`");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
match fs::read_to_string(path) {
|
||||
Ok(content) => Ok(content),
|
||||
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(())
|
||||
},
|
||||
}
|
||||
|
@ -124,10 +124,7 @@ fn inject_deps_into_manifest(
|
|||
) -> std::io::Result<()> {
|
||||
// do not inject deps if we have already done so
|
||||
if cargo_toml.contains(RUSTC_PATH_SECTION) {
|
||||
eprintln!(
|
||||
"warn: dependencies are already setup inside {}, skipping file",
|
||||
manifest_path
|
||||
);
|
||||
eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -142,11 +139,7 @@ fn inject_deps_into_manifest(
|
|||
|
||||
let new_deps = extern_crates.map(|dep| {
|
||||
// format the dependencies that are going to be put inside the Cargo.toml
|
||||
format!(
|
||||
"{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
|
||||
dep = dep,
|
||||
source_path = rustc_source_dir.display()
|
||||
)
|
||||
format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display())
|
||||
});
|
||||
|
||||
// format a new [dependencies]-block with the new deps we need to inject
|
||||
|
@ -163,11 +156,11 @@ fn inject_deps_into_manifest(
|
|||
// etc
|
||||
let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
|
||||
|
||||
// println!("{}", new_manifest);
|
||||
// println!("{new_manifest}");
|
||||
let mut file = File::create(manifest_path)?;
|
||||
file.write_all(new_manifest.as_bytes())?;
|
||||
|
||||
println!("info: successfully setup dependencies inside {}", manifest_path);
|
||||
println!("info: successfully setup dependencies inside {manifest_path}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -214,8 +207,8 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
|
|||
},
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"error: unable to open file `{}` to remove rustc dependencies for {} ({})",
|
||||
project.cargo_file, project.name, err
|
||||
"error: unable to open file `{}` to remove rustc dependencies for {} ({err})",
|
||||
project.cargo_file, project.name
|
||||
);
|
||||
false
|
||||
},
|
||||
|
|
|
@ -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!("vscode tasks successfully installed");
|
||||
},
|
||||
Err(err) => eprintln!(
|
||||
"error: unable to copy `{}` to `{}` ({})",
|
||||
TASK_SOURCE_FILE, TASK_TARGET_FILE, err
|
||||
),
|
||||
Err(err) => eprintln!("error: unable to copy `{TASK_SOURCE_FILE}` to `{TASK_TARGET_FILE}` ({err})"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,23 +41,17 @@ fn check_install_precondition(force_override: bool) -> bool {
|
|||
return delete_vs_task_file(path);
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"error: there is already a `task.json` file inside the `{}` directory",
|
||||
VSCODE_DIR
|
||||
);
|
||||
eprintln!("error: there is already a `task.json` file inside the `{VSCODE_DIR}` directory");
|
||||
println!("info: use the `--force-override` flag to override the existing `task.json` file");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
match fs::create_dir(vs_dir_path) {
|
||||
Ok(_) => {
|
||||
println!("info: created `{}` directory for clippy", VSCODE_DIR);
|
||||
println!("info: created `{VSCODE_DIR}` directory for clippy");
|
||||
},
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"error: the task target directory `{}` could not be created ({})",
|
||||
VSCODE_DIR, err
|
||||
);
|
||||
eprintln!("error: the task target directory `{VSCODE_DIR}` could not be created ({err})");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +73,7 @@ pub fn remove_tasks() {
|
|||
|
||||
fn delete_vs_task_file(path: &Path) -> bool {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,9 +45,8 @@ fn generate_lint_files(
|
|||
renamed_lints: &[RenamedLint],
|
||||
) {
|
||||
let internal_lints = Lint::internal_lints(lints);
|
||||
let usable_lints = Lint::usable_lints(lints);
|
||||
let mut sorted_usable_lints = usable_lints.clone();
|
||||
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
|
||||
let mut usable_lints = Lint::usable_lints(lints);
|
||||
usable_lints.sort_by_key(|lint| lint.name.clone());
|
||||
|
||||
replace_region_in_file(
|
||||
update_mode,
|
||||
|
@ -86,7 +85,7 @@ fn generate_lint_files(
|
|||
)
|
||||
.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, it’s used in `update_lints`",
|
||||
|res| {
|
||||
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)) {
|
||||
let content = gen_lint_group_list(&lint_group, lints.iter());
|
||||
process_file(
|
||||
&format!("clippy_lints/src/lib.register_{}.rs", lint_group),
|
||||
&format!("clippy_lints/src/lib.register_{lint_group}.rs"),
|
||||
update_mode,
|
||||
&content,
|
||||
);
|
||||
|
@ -190,9 +189,9 @@ fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
|
|||
if lints.is_empty() {
|
||||
return false;
|
||||
}
|
||||
println!("{}", header);
|
||||
println!("{header}");
|
||||
for lint in lints.iter().sorted() {
|
||||
println!(" {}", lint);
|
||||
println!(" {lint}");
|
||||
}
|
||||
println!();
|
||||
true
|
||||
|
@ -205,16 +204,16 @@ pub fn print_lints() {
|
|||
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
|
||||
|
||||
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());
|
||||
|
||||
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.
|
||||
|
@ -235,10 +234,10 @@ pub fn print_lints() {
|
|||
#[allow(clippy::too_many_lines)]
|
||||
pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
|
||||
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("::") {
|
||||
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();
|
||||
|
@ -251,14 +250,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
|
|||
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 {
|
||||
old_name: format!("clippy::{}", old_name),
|
||||
old_name: format!("clippy::{old_name}"),
|
||||
new_name: if uplift {
|
||||
new_name.into()
|
||||
} 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.
|
||||
assert!(
|
||||
!renamed_lints.iter().any(|l| lint.old_name == l.old_name),
|
||||
"`{}` has already been renamed",
|
||||
old_name
|
||||
"`{old_name}` has already been renamed"
|
||||
);
|
||||
assert!(
|
||||
!deprecated_lints.iter().any(|l| lint.old_name == l.name),
|
||||
"`{}` has already been deprecated",
|
||||
old_name
|
||||
"`{old_name}` has already been deprecated"
|
||||
);
|
||||
|
||||
// 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 {
|
||||
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
|
||||
println!(
|
||||
"`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
|
||||
old_name
|
||||
"`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually."
|
||||
);
|
||||
} else if found_new_name {
|
||||
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
|
||||
println!(
|
||||
"`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
|
||||
new_name
|
||||
"`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually."
|
||||
);
|
||||
} else {
|
||||
// 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.
|
||||
if try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.rs", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.rs", new_name)),
|
||||
Path::new(&format!("tests/ui/{old_name}.rs")),
|
||||
Path::new(&format!("tests/ui/{new_name}.rs")),
|
||||
) {
|
||||
try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.stderr", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.stderr", new_name)),
|
||||
Path::new(&format!("tests/ui/{old_name}.stderr")),
|
||||
Path::new(&format!("tests/ui/{new_name}.stderr")),
|
||||
);
|
||||
try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.fixed", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.fixed", new_name)),
|
||||
Path::new(&format!("tests/ui/{old_name}.fixed")),
|
||||
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 = if lint.module == old_name
|
||||
&& try_rename_file(
|
||||
Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
|
||||
Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
|
||||
Path::new(&format!("clippy_lints/src/{old_name}.rs")),
|
||||
Path::new(&format!("clippy_lints/src/{new_name}.rs")),
|
||||
) {
|
||||
// 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) {
|
||||
|
@ -356,14 +351,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
|
|||
} else if !lint.module.contains("::")
|
||||
// Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
|
||||
&& try_rename_file(
|
||||
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
|
||||
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
|
||||
Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)),
|
||||
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.
|
||||
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) {
|
||||
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.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);
|
||||
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");
|
||||
|
@ -408,7 +403,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) {
|
|||
});
|
||||
|
||||
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 {
|
||||
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 (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 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) {
|
||||
let test_file_stem = format!("tests/ui/{}", name);
|
||||
let test_file_stem = format!("tests/ui/{name}");
|
||||
let path = Path::new(&test_file_stem);
|
||||
|
||||
// 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()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{}` from `{}`",
|
||||
name,
|
||||
"warn: you will have to manually remove any code related to `{name}` from `{}`",
|
||||
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(), "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {};", name);
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
content = content.replacen(&mod_decl, "", 1);
|
||||
|
||||
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) {
|
||||
if update_mode == UpdateMode::Check {
|
||||
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 {
|
||||
exit_with_failure();
|
||||
}
|
||||
} else {
|
||||
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!(
|
||||
output,
|
||||
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
|
||||
group_name
|
||||
"store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![",
|
||||
);
|
||||
for (module, name) in details {
|
||||
let _ = writeln!(output, " LintId::of({}::{}),", module, name);
|
||||
let _ = writeln!(output, " LintId::of({module}::{name}),");
|
||||
}
|
||||
output.push_str("])\n");
|
||||
|
||||
|
@ -783,7 +776,7 @@ fn gen_register_lint_list<'a>(
|
|||
if !is_public {
|
||||
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");
|
||||
|
||||
|
@ -841,7 +834,7 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
|
|||
for (rel_path, file) in clippy_lints_src_files() {
|
||||
let path = file.path();
|
||||
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
|
||||
.components()
|
||||
.map(|c| c.as_os_str().to_str().unwrap())
|
||||
|
@ -1050,7 +1043,7 @@ fn remove_line_splices(s: &str) -> String {
|
|||
.trim_matches('#')
|
||||
.strip_prefix('"')
|
||||
.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());
|
||||
unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
|
||||
if ch.is_ok() {
|
||||
|
@ -1076,10 +1069,10 @@ fn replace_region_in_file(
|
|||
end: &str,
|
||||
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) {
|
||||
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 {
|
||||
|
@ -1087,7 +1080,7 @@ fn replace_region_in_file(
|
|||
UpdateMode::Check => (),
|
||||
UpdateMode::Change => {
|
||||
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)]
|
||||
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>) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.65"
|
||||
version = "0.1.66"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -18,7 +18,7 @@ quine-mc_cluskey = "0.2"
|
|||
regex-syntax = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
tempfile = { version = "3.3.0", optional = true }
|
||||
toml = "0.5"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
|
|
|
@ -92,7 +92,7 @@ impl ApproxConstant {
|
|||
cx,
|
||||
APPROX_CONSTANT,
|
||||
e.span,
|
||||
&format!("approximate value of `{}::consts::{}` found", module, &name),
|
||||
&format!("approximate value of `{module}::consts::{}` found", &name),
|
||||
None,
|
||||
"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
|
||||
true
|
||||
} else {
|
||||
let round_const = format!("{:.*}", value.len() - 2, constant);
|
||||
let round_const = format!("{constant:.*}", value.len() - 2);
|
||||
value == round_const
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr
|
|||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("{} x86 assembly syntax used", style),
|
||||
&format!("{style} x86 assembly syntax used"),
|
||||
None,
|
||||
&format!("use {} x86 assembly syntax", !style),
|
||||
);
|
||||
|
@ -64,6 +64,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
|
@ -72,6 +73,7 @@ declare_clippy_lint! {
|
|||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
|
@ -103,6 +105,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
|
@ -111,6 +114,7 @@ declare_clippy_lint! {
|
|||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
|
|
|
@ -60,9 +60,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
|
|||
cx,
|
||||
ASSERTIONS_ON_CONSTANTS,
|
||||
macro_call.span,
|
||||
&format!("`assert!(false{})` should probably be replaced", assert_arg),
|
||||
&format!("`assert!(false{assert_arg})` should probably be replaced"),
|
||||
None,
|
||||
&format!("use `panic!({})` or `unreachable!({0})`", panic_arg),
|
||||
&format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
|||
"called `assert!` with `Result::is_ok`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.unwrap(){}",
|
||||
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
|
||||
semicolon
|
||||
"{}.unwrap(){semicolon}",
|
||||
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
|
||||
),
|
||||
app,
|
||||
);
|
||||
|
@ -84,9 +83,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
|||
"called `assert!` with `Result::is_err`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.unwrap_err(){}",
|
||||
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0,
|
||||
semicolon
|
||||
"{}.unwrap_err(){semicolon}",
|
||||
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
|
||||
),
|
||||
app,
|
||||
);
|
||||
|
|
|
@ -541,10 +541,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
|
|||
cx,
|
||||
INLINE_ALWAYS,
|
||||
attr.span,
|
||||
&format!(
|
||||
"you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
|
||||
name
|
||||
),
|
||||
&format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -720,7 +717,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
let mut unix_suggested = false;
|
||||
|
||||
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);
|
||||
|
||||
if !unix_suggested && is_unix(os) {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::{Namespace, Res};
|
||||
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_middle::ty::GeneratorInteriorTypeCause;
|
||||
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! {
|
||||
/// ### What it does
|
||||
|
@ -171,12 +172,12 @@ impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF,
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct AwaitHolding {
|
||||
conf_invalid_types: Vec<DisallowedType>,
|
||||
def_ids: FxHashMap<DefId, DisallowedType>,
|
||||
conf_invalid_types: Vec<DisallowedPath>,
|
||||
def_ids: FxHashMap<DefId, DisallowedPath>,
|
||||
}
|
||||
|
||||
impl AwaitHolding {
|
||||
pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
|
||||
pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self {
|
||||
Self {
|
||||
conf_invalid_types,
|
||||
def_ids: FxHashMap::default(),
|
||||
|
@ -187,11 +188,8 @@ impl AwaitHolding {
|
|||
impl LateLintPass<'_> for AwaitHolding {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for conf in &self.conf_invalid_types {
|
||||
let path = match conf {
|
||||
DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
|
||||
};
|
||||
let segs: Vec<_> = path.split("::").collect();
|
||||
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
|
||||
self.def_ids.insert(id, conf.clone());
|
||||
}
|
||||
}
|
||||
|
@ -256,29 +254,27 @@ impl AwaitHolding {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
|
||||
let (type_name, reason) = match disallowed {
|
||||
DisallowedType::Simple(path) => (path, &None),
|
||||
DisallowedType::WithReason { path, reason } => (path, reason),
|
||||
};
|
||||
|
||||
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
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| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason.clone());
|
||||
if let Some(reason) = disallowed.reason() {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
match_def_path(cx, def_id, &paths::MUTEX_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
|
||||
cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
|
||||
|| 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_RWLOCK_READ_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
|
||||
|
|
|
@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr;
|
|||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_block_with_applicability;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind};
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -44,39 +45,6 @@ declare_clippy_lint! {
|
|||
|
||||
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 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`";
|
||||
|
@ -117,8 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
let span =
|
||||
block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
if span.from_expansion() || expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
@ -145,11 +112,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let mut visitor = ExVisitor { found_block: None, cx };
|
||||
walk_expr(&mut visitor, cond);
|
||||
if let Some(block) = visitor.found_block {
|
||||
span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE);
|
||||
}
|
||||
let _: Option<!> = for_each_expr(cond, |e| {
|
||||
if let ExprKind::Closure(closure) = e.kind {
|
||||
// do not lint if the closure is called using an iterator (see #1141)
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,9 +98,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
|
|||
cx,
|
||||
BOOL_ASSERT_COMPARISON,
|
||||
macro_call.span,
|
||||
&format!("used `{}!` with a literal bool", macro_name),
|
||||
&format!("used `{macro_name}!` with a literal bool"),
|
||||
"replace it with",
|
||||
format!("{}!(..)", non_eq_mac),
|
||||
format!("{non_eq_mac}!(..)"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hir::{Block, ExprKind};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
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;
|
||||
|
||||
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(else_lit) = int_literal(else_)
|
||||
{
|
||||
let inverted = if
|
||||
check_int_literal_equals_val(then_lit, 1)
|
||||
&& check_int_literal_equals_val(else_lit, 0) {
|
||||
let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
|
||||
false
|
||||
} else if
|
||||
check_int_literal_equals_val(then_lit, 0)
|
||||
&& check_int_literal_equals_val(else_lit, 1) {
|
||||
} else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
|
||||
true
|
||||
} else {
|
||||
// Expression isn't boolean, exit
|
||||
|
@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,9 +263,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
|||
}
|
||||
.and_then(|op| {
|
||||
Some(format!(
|
||||
"{}{}{}",
|
||||
"{}{op}{}",
|
||||
snippet_opt(cx, lhs.span)?,
|
||||
op,
|
||||
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();
|
||||
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,
|
||||
}
|
||||
|
|
61
clippy_lints/src/box_default.rs
Normal file
61
clippy_lints/src/box_default.rs
Normal 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,10 +57,8 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
|
|||
},
|
||||
DUMMY_SP,
|
||||
&format!(
|
||||
"the \"{}\" {} in the feature name \"{}\" is {}",
|
||||
substring,
|
||||
"the \"{substring}\" {} in the feature name \"{feature}\" is {}",
|
||||
if is_prefix { "prefix" } else { "suffix" },
|
||||
feature,
|
||||
if is_negative { "negative" } else { "redundant" }
|
||||
),
|
||||
None,
|
||||
|
|
|
@ -196,7 +196,7 @@ impl LateLintPass<'_> for Cargo {
|
|||
},
|
||||
Err(e) => {
|
||||
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) => {
|
||||
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}"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
|||
cx,
|
||||
MULTIPLE_CRATE_VERSIONS,
|
||||
DUMMY_SP,
|
||||
&format!("multiple versions for dependency `{}`: {}", name, versions),
|
||||
&format!("multiple versions for dependency `{name}`: {versions}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
|
|||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
|
||||
format!("{core_or_std}::ptr::{macro_name}!({snip})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,15 +41,9 @@ pub(super) fn check(
|
|||
);
|
||||
|
||||
let message = if cast_from.is_bool() {
|
||||
format!(
|
||||
"casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`",
|
||||
cast_from, cast_to
|
||||
)
|
||||
format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`")
|
||||
} else {
|
||||
format!(
|
||||
"casting `{}` to `{}` may become silently lossy if you later change the type",
|
||||
cast_from, cast_to
|
||||
)
|
||||
format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
@ -58,7 +52,7 @@ pub(super) fn check(
|
|||
expr.span,
|
||||
&message,
|
||||
"try",
|
||||
format!("{}::from({})", cast_to, sugg),
|
||||
format!("{cast_to}::from({sugg})"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -103,10 +103,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
return;
|
||||
}
|
||||
|
||||
format!(
|
||||
"casting `{}` to `{}` may truncate the value{}",
|
||||
cast_from, cast_to, suffix,
|
||||
)
|
||||
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
|
||||
},
|
||||
|
||||
(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,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting `{}::{}` to `{}` will truncate the value{}",
|
||||
cast_from, variant.name, cast_to, suffix,
|
||||
"casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}",
|
||||
variant.name,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
format!(
|
||||
"casting `{}` to `{}` may truncate the value{}",
|
||||
cast_from, cast_to, suffix,
|
||||
)
|
||||
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
|
||||
},
|
||||
|
||||
(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)) => {
|
||||
|
|
|
@ -35,10 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
|
|||
cx,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting `{}` to `{}` may wrap around the value{}",
|
||||
cast_from, cast_to, suffix,
|
||||
),
|
||||
&format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
|
|||
CAST_PTR_ALIGNMENT,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
|
||||
cast_from,
|
||||
cast_to,
|
||||
"casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)",
|
||||
from_layout.align.abi.bytes(),
|
||||
to_layout.align.abi.bytes(),
|
||||
),
|
||||
|
|
|
@ -14,10 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c
|
|||
cx,
|
||||
CAST_SIGN_LOSS,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting `{}` to `{}` may lose the sign of the value",
|
||||
cast_from, cast_to
|
||||
),
|
||||
&format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio
|
|||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
|
||||
start_ty.ty, from_size, end_ty.ty, to_size,
|
||||
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
|
||||
start_ty.ty, end_ty.ty,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
|
|
|
@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"use a byte literal instead",
|
||||
format!("b{}", snippet),
|
||||
format!("b{snippet}"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
cx,
|
||||
FN_TO_NUMERIC_CAST,
|
||||
expr.span,
|
||||
&format!("casting function pointer `{}` to `{}`", from_snippet, cast_to),
|
||||
&format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
|
||||
"try",
|
||||
format!("{} as usize", from_snippet),
|
||||
format!("{from_snippet} as usize"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
cx,
|
||||
FN_TO_NUMERIC_CAST_ANY,
|
||||
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?",
|
||||
format!("{}() as {}", from_snippet, cast_to),
|
||||
format!("{from_snippet}() as {cast_to}"),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -24,12 +24,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
cx,
|
||||
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting function pointer `{}` to `{}`, which truncates the value",
|
||||
from_snippet, cast_to
|
||||
),
|
||||
&format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"),
|
||||
"try",
|
||||
format!("{} as usize", from_snippet),
|
||||
format!("{from_snippet} as usize"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVer
|
|||
let turbofish = match &cast_to_hir_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(
|
||||
cx,
|
||||
|
@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVer
|
|||
expr.span,
|
||||
"`as` casting between raw pointers without changing its mutability",
|
||||
"try `pointer::cast`, a safer alternative",
|
||||
format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish),
|
||||
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::numeric_literal::NumericLiteral;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
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) {
|
||||
let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
let literal_str = &cast_str;
|
||||
|
||||
if_chain! {
|
||||
if let LitKind::Int(n, _) = lit.node;
|
||||
|
@ -49,12 +52,16 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
match lit.node {
|
||||
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() => {
|
||||
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::Float(_, LitFloatType::Suffixed(_))
|
||||
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(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
|
||||
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
|
||||
}
|
||||
|
||||
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 replaced_literal;
|
||||
let matchless = if literal_str.contains(['(', ')']) {
|
||||
replaced_literal = literal_str.replace(['(', ')'], "");
|
||||
&replaced_literal
|
||||
} else {
|
||||
literal_str
|
||||
// first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1`
|
||||
let literal_str = raw_literal_str
|
||||
.replace(['(', ')'], "")
|
||||
.trim_end_matches('.')
|
||||
.to_string();
|
||||
// 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(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
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",
|
||||
format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
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 rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
|||
item.span,
|
||||
"checked cast can be simplified",
|
||||
"try",
|
||||
format!("{}::try_from({}).is_ok()", to_type, snippet),
|
||||
format!("{to_type}::try_from({snippet}).is_ok()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
|||
|
||||
/// Check for `expr >= 0`
|
||||
fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref lit) = &check.kind;
|
||||
if let LitKind::Int(0, _) = &lit.node;
|
||||
|
||||
then {
|
||||
Some(Conversion::new_any(candidate))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
is_integer_literal(check, 0).then(|| Conversion::new_any(candidate))
|
||||
}
|
||||
|
||||
/// Check for `expr >= (to_type::MIN as from_type)`
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::LimitStack;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
@ -61,11 +63,27 @@ impl CognitiveComplexity {
|
|||
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_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) {
|
||||
returns
|
||||
|
@ -74,13 +92,12 @@ impl CognitiveComplexity {
|
|||
(returns / 2)
|
||||
};
|
||||
|
||||
let mut rust_cc = cc;
|
||||
// prevent degenerate cases where unreachable code contains `return` statements
|
||||
if rust_cc >= ret_adjust {
|
||||
rust_cc -= ret_adjust;
|
||||
if cc >= ret_adjust {
|
||||
cc -= ret_adjust;
|
||||
}
|
||||
|
||||
if rust_cc > self.limit.limit() {
|
||||
if cc > self.limit.limit() {
|
||||
let fn_span = match kind {
|
||||
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
|
||||
FnKind::Closure => {
|
||||
|
@ -107,8 +124,7 @@ impl CognitiveComplexity {
|
|||
COGNITIVE_COMPLEXITY,
|
||||
fn_span,
|
||||
&format!(
|
||||
"the function has a cognitive complexity of ({}/{})",
|
||||
rust_cc,
|
||||
"the function has a cognitive complexity of ({cc}/{})",
|
||||
self.limit.limit()
|
||||
),
|
||||
None,
|
||||
|
@ -141,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
|
|||
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,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
cx,
|
||||
DEFAULT_TRAIT_ACCESS,
|
||||
expr.span,
|
||||
&format!("calling `{}` is more clear than this expression", replacement),
|
||||
&format!("calling `{replacement}` is more clear than this expression"),
|
||||
"try",
|
||||
replacement,
|
||||
Applicability::Unspecified, // First resolve the TODO above
|
||||
|
@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
.map(|(field, rhs)| {
|
||||
// extract and store the assigned value for help message
|
||||
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
|
||||
format!("{}: {}", field, value_snippet)
|
||||
format!("{field}: {value_snippet}")
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
format!("{}::<{}>", adt_def_ty_name, &tys_str)
|
||||
format!("{adt_def_ty_name}::<{}>", &tys_str)
|
||||
} else {
|
||||
binding_type.to_string()
|
||||
}
|
||||
|
@ -235,12 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
|
||||
let sugg = if ext_with_default {
|
||||
if field_list.is_empty() {
|
||||
format!("{}::default()", binding_type)
|
||||
format!("{binding_type}::default()")
|
||||
} else {
|
||||
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
|
||||
format!("{binding_type} {{ {field_list}, ..Default::default() }}")
|
||||
}
|
||||
} else {
|
||||
format!("{} {{ {} }}", binding_type, field_list)
|
||||
format!("{binding_type} {{ {field_list} }}")
|
||||
};
|
||||
|
||||
// span lint once per statement that binds default
|
||||
|
@ -250,10 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
first_assign.unwrap().span,
|
||||
"field assignment outside of initializer for an instance created with Default::default()",
|
||||
Some(local.span),
|
||||
&format!(
|
||||
"consider initializing the variable with `{}` and removing relevant reassignments",
|
||||
sugg
|
||||
),
|
||||
&format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"),
|
||||
);
|
||||
self.reassigned_linted.insert(span);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ declare_clippy_lint! {
|
|||
/// let _ = std::iter::empty::<usize>();
|
||||
/// 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,
|
||||
style,
|
||||
"check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
|
||||
|
|
|
@ -95,8 +95,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
src
|
||||
} else {
|
||||
match lit.node {
|
||||
LitKind::Int(src, _) => format!("{}", src),
|
||||
LitKind::Float(src, _) => format!("{}", src),
|
||||
LitKind::Int(src, _) => format!("{src}"),
|
||||
LitKind::Float(src, _) => format!("{src}"),
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir::{self as hir, HirId, Item, ItemKind};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -135,7 +135,7 @@ declare_clippy_lint! {
|
|||
/// let x = String::new();
|
||||
/// let y: &str = &x;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub EXPLICIT_AUTO_DEREF,
|
||||
complexity,
|
||||
"dereferencing when the compiler would automatically dereference"
|
||||
|
@ -184,6 +184,7 @@ impl Dereferencing {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StateData {
|
||||
/// Span of the top level expression
|
||||
span: Span,
|
||||
|
@ -191,12 +192,14 @@ struct StateData {
|
|||
position: Position,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DerefedBorrow {
|
||||
count: usize,
|
||||
msg: &'static str,
|
||||
snip_expr: Option<HirId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
// Any number of deref method calls.
|
||||
DerefMethod {
|
||||
|
@ -276,10 +279,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
(None, kind) => {
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
let (position, adjustments) = walk_parents(cx, expr, self.msrv);
|
||||
|
||||
match kind {
|
||||
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)
|
||||
{
|
||||
self.state = Some((
|
||||
|
@ -451,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
(Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
|
||||
let position = data.position;
|
||||
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)
|
||||
{
|
||||
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.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Position {
|
||||
MethodReceiver,
|
||||
/// The method is defined on a reference type. e.g. `impl Foo for &T`
|
||||
MethodReceiverRefImpl,
|
||||
Callee,
|
||||
ImplArg(HirId),
|
||||
FieldAccess(Symbol),
|
||||
FieldAccess {
|
||||
name: Symbol,
|
||||
of_union: bool,
|
||||
}, // union fields cannot be auto borrowed
|
||||
Postfix,
|
||||
Deref,
|
||||
/// Any other location which will trigger auto-deref to a specific time.
|
||||
|
@ -645,7 +653,10 @@ impl Position {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -657,7 +668,7 @@ impl Position {
|
|||
Self::MethodReceiver
|
||||
| Self::MethodReceiverRefImpl
|
||||
| Self::Callee
|
||||
| Self::FieldAccess(_)
|
||||
| Self::FieldAccess { .. }
|
||||
| Self::Postfix => PREC_POSTFIX,
|
||||
Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
|
||||
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::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
|
||||
| ExprKind::Index(child, _)
|
||||
|
@ -865,6 +879,13 @@ fn walk_parents<'tcx>(
|
|||
(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>(
|
||||
cx: &LateContext<'tcx>,
|
||||
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 {
|
||||
format!("({})", expr_str)
|
||||
format!("({expr_str})")
|
||||
} else {
|
||||
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",
|
||||
},
|
||||
"try this",
|
||||
format!("{}{}{}", addr_of_str, deref_str, expr_str),
|
||||
format!("{addr_of_str}{deref_str}{expr_str}"),
|
||||
app,
|
||||
);
|
||||
},
|
||||
|
@ -1336,7 +1357,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||
&& !has_enclosing_paren(&snip)
|
||||
&& (expr.precedence().order() < data.position.precedence() || calls_field)
|
||||
{
|
||||
format!("({})", snip)
|
||||
format!("({snip})")
|
||||
} else {
|
||||
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 sugg =
|
||||
if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
|
||||
format!("{}({})", prefix, snip)
|
||||
format!("{prefix}({snip})")
|
||||
} else {
|
||||
format!("{}{}", prefix, snip)
|
||||
format!("{prefix}{snip}")
|
||||
};
|
||||
diag.span_suggestion(data.span, "try this", sugg, app);
|
||||
},
|
||||
|
@ -1460,14 +1481,14 @@ impl Dereferencing {
|
|||
} else {
|
||||
pat.always_deref = false;
|
||||
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() => {
|
||||
// Double reference might be needed at this point.
|
||||
pat.always_deref = false;
|
||||
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
|
||||
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
|
||||
|
|
|
@ -191,7 +191,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
style,
|
||||
nursery,
|
||||
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
|
||||
}
|
||||
|
||||
|
|
151
clippy_lints/src/disallowed_macros.rs
Normal file
151
clippy_lints/src/disallowed_macros.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
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_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
|
@ -58,12 +60,12 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedMethods {
|
||||
conf_disallowed: Vec<conf::DisallowedMethod>,
|
||||
conf_disallowed: Vec<conf::DisallowedPath>,
|
||||
disallowed: DefIdMap<usize>,
|
||||
}
|
||||
|
||||
impl DisallowedMethods {
|
||||
pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
|
||||
pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
|
||||
Self {
|
||||
conf_disallowed,
|
||||
disallowed: DefIdMap::default(),
|
||||
|
@ -77,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
|||
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) {
|
||||
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) {
|
||||
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());
|
||||
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
|
||||
if let conf::DisallowedMethod::WithReason {
|
||||
reason: Some(reason), ..
|
||||
} = conf
|
||||
{
|
||||
diag.note(&format!("{} (from clippy.toml)", reason));
|
||||
if let Some(reason) = conf.reason() {
|
||||
diag.note(&format!("{reason} (from clippy.toml)"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -99,8 +99,7 @@ impl EarlyLintPass for DisallowedScriptIdents {
|
|||
DISALLOWED_SCRIPT_IDENTS,
|
||||
span,
|
||||
&format!(
|
||||
"identifier `{}` has a Unicode script that is not allowed by configuration: {}",
|
||||
symbol_str,
|
||||
"identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}",
|
||||
script.full_name()
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{
|
||||
def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind,
|
||||
};
|
||||
use rustc_hir::def::{Namespace, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
@ -52,13 +52,13 @@ declare_clippy_lint! {
|
|||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedTypes {
|
||||
conf_disallowed: Vec<conf::DisallowedType>,
|
||||
conf_disallowed: Vec<conf::DisallowedPath>,
|
||||
def_ids: FxHashMap<DefId, Option<String>>,
|
||||
prim_tys: FxHashMap<PrimTy, Option<String>>,
|
||||
}
|
||||
|
||||
impl DisallowedTypes {
|
||||
pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
|
||||
pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
|
||||
Self {
|
||||
conf_disallowed,
|
||||
def_ids: FxHashMap::default(),
|
||||
|
@ -88,15 +88,9 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
|||
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for conf in &self.conf_disallowed {
|
||||
let (path, reason) = match conf {
|
||||
conf::DisallowedType::Simple(path) => (path, None),
|
||||
conf::DisallowedType::WithReason { path, reason } => (
|
||||
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) {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)"));
|
||||
match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
|
||||
Res::Def(_, id) => {
|
||||
self.def_ids.insert(id, reason);
|
||||
},
|
||||
|
@ -130,7 +124,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
|
|||
cx,
|
||||
DISALLOWED_TYPES,
|
||||
span,
|
||||
&format!("`{}` is not allowed according to config", name),
|
||||
&format!("`{name}` is not allowed according to config"),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
|
|
|
@ -198,6 +198,29 @@ declare_clippy_lint! {
|
|||
"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)]
|
||||
#[derive(Clone)]
|
||||
pub struct DocMarkdown {
|
||||
|
@ -214,9 +237,14 @@ impl DocMarkdown {
|
|||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DocMarkdown =>
|
||||
[DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
|
||||
);
|
||||
impl_lint_pass!(DocMarkdown => [
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
MISSING_SAFETY_DOC,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
|
@ -237,7 +265,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
panic_span: None,
|
||||
};
|
||||
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_) => {
|
||||
|
@ -287,7 +323,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
panic_span: None,
|
||||
};
|
||||
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)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct DocHeaders {
|
||||
safety: bool,
|
||||
errors: bool,
|
||||
|
@ -460,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
|
|||
}
|
||||
|
||||
if doc.is_empty() {
|
||||
return DocHeaders {
|
||||
safety: false,
|
||||
errors: false,
|
||||
panics: false,
|
||||
};
|
||||
return DocHeaders::default();
|
||||
}
|
||||
|
||||
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::{CodeBlockKind, CowStr};
|
||||
|
||||
let mut headers = DocHeaders {
|
||||
safety: false,
|
||||
errors: false,
|
||||
panics: false,
|
||||
};
|
||||
let mut headers = DocHeaders::default();
|
||||
let mut in_code = false;
|
||||
let mut in_link = None;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
|
||||
// Adjust for the beginning of the current `Event`
|
||||
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
|
||||
text_to_check.push((text, span));
|
||||
|
@ -606,6 +643,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
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) {
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
|
||||
Ok(o) => o,
|
||||
|
@ -790,7 +848,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
|
|||
diag.span_suggestion_with_style(
|
||||
span,
|
||||
"try",
|
||||
format!("`{}`", snippet),
|
||||
format!("`{snippet}`"),
|
||||
applicability,
|
||||
// always show the suggestion in a separate line, since the
|
||||
// inline presentation adds another pair of backticks
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
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::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_session::{declare_lint_pass, declare_tool_lint};
|
||||
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 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 {
|
||||
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_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY),
|
||||
sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_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 => (FORGET_COPY, FORGET_COPY_SUMMARY),
|
||||
sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
@ -221,7 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
sym::mem_drop
|
||||
if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|
||||
|| 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)
|
||||
},
|
||||
|
@ -236,8 +241,23 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
expr.span,
|
||||
msg,
|
||||
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
|
||||
}
|
||||
|
|
|
@ -113,13 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
),
|
||||
};
|
||||
format!(
|
||||
"if let {}::{} = {}.entry({}) {} else {}",
|
||||
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
|
||||
map_ty.entry_path(),
|
||||
entry_kind,
|
||||
map_str,
|
||||
key_str,
|
||||
then_str,
|
||||
else_str,
|
||||
)
|
||||
} else {
|
||||
// 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 = indent_str.as_deref().unwrap_or("");
|
||||
format!(
|
||||
"match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
|
||||
{indent} {entry}::{} => {}\n{indent}}}",
|
||||
map_str,
|
||||
key_str,
|
||||
then_entry,
|
||||
"match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
|
||||
{indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
|
||||
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
|
||||
else_entry,
|
||||
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
|
||||
entry = map_ty.entry_path(),
|
||||
indent = indent_str,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -163,20 +153,16 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
then_search.snippet_occupied(cx, then_expr.span, &mut app)
|
||||
};
|
||||
format!(
|
||||
"if let {}::{} = {}.entry({}) {}",
|
||||
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
|
||||
map_ty.entry_path(),
|
||||
entry_kind,
|
||||
map_str,
|
||||
key_str,
|
||||
body_str,
|
||||
)
|
||||
} 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;
|
||||
if contains_expr.negated {
|
||||
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 {
|
||||
format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
|
||||
format!("{map_str}.entry({key_str}).or_insert({value_str});")
|
||||
}
|
||||
} else {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
|
@ -186,7 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
} else {
|
||||
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
|
||||
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 {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
// This would need to be a different lint.
|
||||
|
|
|
@ -202,12 +202,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
|
|||
cx,
|
||||
ENUM_VARIANT_NAMES,
|
||||
span,
|
||||
&format!("all variants have the same {}fix: `{}`", what, value),
|
||||
&format!("all variants have the same {what}fix: `{value}`"),
|
||||
None,
|
||||
&format!(
|
||||
"remove the {}fixes and use full paths to \
|
||||
the variants instead of glob imports",
|
||||
what
|
||||
"remove the {what}fixes and use full paths to \
|
||||
the variants instead of glob imports"
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,9 +51,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
|
|||
false
|
||||
},
|
||||
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
|
||||
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => {
|
||||
!etc.as_opt_usize().is_some() && array_rec(a)
|
||||
}
|
||||
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
|
||||
PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
|
||||
PatKind::Path(_) | PatKind::Lit(_) => true,
|
||||
}
|
||||
|
@ -93,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
|
|||
"this pattern matching can be expressed using equality",
|
||||
"try",
|
||||
format!(
|
||||
"{} == {}",
|
||||
"{} == {pat_str}",
|
||||
snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
|
||||
pat_str,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir;
|
||||
use rustc_hir::intravisit;
|
||||
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_lint::{LateContext, LateLintPass};
|
||||
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::symbol::kw;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
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> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher::VecArgs;
|
||||
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::{higher, is_adjusted, path_to_local, path_to_local_id};
|
||||
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_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
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_span::symbol::sym;
|
||||
|
||||
|
@ -122,15 +122,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
then {
|
||||
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
if_chain! {
|
||||
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
|
||||
if substs.as_closure().kind() == ClosureKind::FnMut;
|
||||
if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
|
||||
|
||||
then {
|
||||
if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
|
||||
&& implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[])
|
||||
&& path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
|
||||
{
|
||||
// Mutable closure is used after current expr; we cannot consume it.
|
||||
snippet = format!("&mut {}", snippet);
|
||||
}
|
||||
snippet = format!("&mut {snippet}");
|
||||
}
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
|
@ -157,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace the closure with the method itself",
|
||||
format!("{}::{}", name, path.ident.name),
|
||||
format!("{name}::{}", path.ident.name),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
})
|
||||
|
|
|
@ -97,7 +97,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
|
|||
item.span,
|
||||
msg,
|
||||
|diag| {
|
||||
let sugg = format!("#[non_exhaustive]\n{}", indent);
|
||||
let sugg = format!("#[non_exhaustive]\n{indent}");
|
||||
diag.span_suggestion(suggestion_span,
|
||||
"try adding #[non_exhaustive]",
|
||||
sugg,
|
||||
|
|
|
@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
// used.
|
||||
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"),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
format!("{}().write_fmt(...)", dest_name),
|
||||
format!("{dest_name}().write_fmt(...)"),
|
||||
"print".into(),
|
||||
)
|
||||
};
|
||||
|
@ -100,9 +100,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
cx,
|
||||
EXPLICIT_WRITE,
|
||||
expr.span,
|
||||
&format!("use of `{}.unwrap()`", used),
|
||||
&format!("use of `{used}.unwrap()`"),
|
||||
"try this",
|
||||
format!("{}{}!({})", prefix, sugg_mac, inputs_snippet),
|
||||
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -173,9 +173,9 @@ impl FloatFormat {
|
|||
T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
|
||||
{
|
||||
match self {
|
||||
Self::LowerExp => format!("{:e}", f),
|
||||
Self::UpperExp => format!("{:E}", f),
|
||||
Self::Normal => format!("{}", f),
|
||||
Self::LowerExp => format!("{f:e}"),
|
||||
Self::UpperExp => format!("{f:E}"),
|
||||
Self::Normal => format!("{f}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
then {
|
||||
let op = format!(
|
||||
"{}{}{}",
|
||||
suggestion,
|
||||
"{suggestion}{}{}",
|
||||
// Check for float literals without numbers following the decimal
|
||||
// separator such as `2.` and adds a trailing zero
|
||||
if sym.as_str().ends_with('.') {
|
||||
|
@ -172,7 +171,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar
|
|||
expr.span,
|
||||
"logarithm for bases 2, 10 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method),
|
||||
format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -251,7 +250,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
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 };
|
||||
|
||||
// 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(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
|
@ -329,8 +339,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_par(),
|
||||
Sugg::hir(cx, receiver, ".."),
|
||||
Sugg::hir(cx, other_addend, ".."),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
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<'_>) {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Add, ..
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
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) {
|
||||
(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) {
|
||||
(inner_lhs, inner_rhs, lhs)
|
||||
(
|
||||
inner_lhs,
|
||||
maybe_neg_sugg(inner_rhs),
|
||||
Sugg::hir(cx, lhs, "..").to_string(),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
@ -472,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
expr.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
prepare_receiver_sugg(cx, recv),
|
||||
Sugg::hir(cx, arg1, ".."),
|
||||
Sugg::hir(cx, arg2, ".."),
|
||||
),
|
||||
format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
|||
[_] => {
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
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);
|
||||
},
|
||||
[..] => {},
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
|
||||
use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
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};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -64,7 +66,67 @@ declare_clippy_lint! {
|
|||
"`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 {
|
||||
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_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 {
|
||||
|
@ -99,12 +224,7 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_format_in_format_args(
|
||||
cx: &LateContext<'_>,
|
||||
call_site: Span,
|
||||
name: Symbol,
|
||||
arg: &Expr<'_>,
|
||||
) {
|
||||
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
|
||||
let expn_data = arg.span.ctxt().outer_expn_data();
|
||||
if expn_data.call_site.from_expansion() {
|
||||
return;
|
||||
|
@ -117,11 +237,10 @@ fn check_format_in_format_args(
|
|||
cx,
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
call_site,
|
||||
&format!("`format!` in `{}!` args", name),
|
||||
&format!("`format!` in `{name}!` args"),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
"combine the `format!(..)` arguments with the outer `{}!(..)` call",
|
||||
name
|
||||
"combine the `format!(..)` arguments with the outer `{name}!(..)` call"
|
||||
));
|
||||
diag.help("or consider changing `format!` to `format_args!`");
|
||||
},
|
||||
|
@ -149,8 +268,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span.with_lo(receiver.span.hi()),
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
"`to_string` applied to a type that implements `Display` in `{name}!` args"
|
||||
),
|
||||
"remove this",
|
||||
String::new(),
|
||||
|
@ -162,16 +280,13 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
"`to_string` applied to a type that implements `Display` in `{name}!` args"
|
||||
),
|
||||
"use this",
|
||||
format!(
|
||||
"{}{:*>width$}{}",
|
||||
"{}{:*>n_needed_derefs$}{receiver_snippet}",
|
||||
if needs_ref { "&" } else { "" },
|
||||
"",
|
||||
receiver_snippet,
|
||||
width = n_needed_derefs
|
||||
""
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -180,9 +295,12 @@ 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 {
|
||||
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()
|
||||
}
|
||||
|
||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||
|
@ -192,7 +310,11 @@ where
|
|||
let mut n_total = 0;
|
||||
let mut n_needed = 0;
|
||||
loop {
|
||||
if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
|
||||
if let Some(Adjustment {
|
||||
kind: Adjust::Deref(overloaded_deref),
|
||||
target,
|
||||
}) = iter.next()
|
||||
{
|
||||
n_total += 1;
|
||||
if overloaded_deref.is_some() {
|
||||
n_needed = n_total;
|
||||
|
|
|
@ -214,12 +214,12 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
|
|||
cx,
|
||||
PRINT_IN_FORMAT_IMPL,
|
||||
macro_call.span,
|
||||
&format!("use of `{}!` in `{}` impl", name, impl_trait.name),
|
||||
&format!("use of `{name}!` in `{}` impl", impl_trait.name),
|
||||
"replace with",
|
||||
if let Some(formatter_name) = impl_trait.formatter_name {
|
||||
format!("{}!({}, ..)", replacement, formatter_name)
|
||||
format!("{replacement}!({formatter_name}, ..)")
|
||||
} else {
|
||||
format!("{}!(..)", replacement)
|
||||
format!("{replacement}!(..)")
|
||||
},
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
|
|
|
@ -154,11 +154,10 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
eqop_span,
|
||||
&format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
really are doing `.. = ({op} ..)`",
|
||||
op = op
|
||||
really are doing `.. = ({op} ..)`"
|
||||
),
|
||||
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,
|
||||
eqop_span,
|
||||
&format!(
|
||||
"by not having a space between `{binop}` and `{unop}` it looks like \
|
||||
`{binop}{unop}` is a single operator",
|
||||
binop = binop_str,
|
||||
unop = unop_str
|
||||
"by not having a space between `{binop_str}` and `{unop_str}` it looks like \
|
||||
`{binop_str}{unop_str}` is a single operator"
|
||||
),
|
||||
None,
|
||||
&format!(
|
||||
"put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
|
||||
binop = binop_str,
|
||||
unop = unop_str
|
||||
"put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -246,12 +241,11 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
cx,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
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,
|
||||
&format!(
|
||||
"to remove this lint, remove the `else` or remove the new line between \
|
||||
`else` and `{}`",
|
||||
else_desc,
|
||||
`else` and `{else_desc}`",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -320,11 +314,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
|
|||
cx,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
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,
|
||||
&format!(
|
||||
"to remove this lint, add the missing `else` or add a new line before {}",
|
||||
next_thing,
|
||||
"to remove this lint, add the missing `else` or add a new line before {next_thing}",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_integer_literal;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
|
@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
|||
if pathseg.ident.name.as_str() == "from_str_radix";
|
||||
|
||||
// check if the second argument is a primitive `10`
|
||||
if let ExprKind::Lit(lit) = &radix.kind;
|
||||
if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
|
||||
if is_integer_literal(radix, 10);
|
||||
|
||||
then {
|
||||
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
|
||||
|
@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
|||
exp.span,
|
||||
"this call to `from_str_radix` can be replaced with a call to `str::parse`",
|
||||
"try",
|
||||
format!("{}.parse::<{}>()", sugg, prim_ty.name_str()),
|
||||
format!("{sugg}.parse::<{}>()", prim_ty.name_str()),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::Applicability;
|
||||
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_middle::{
|
||||
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::source::snippet_opt;
|
||||
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 core::ops::ControlFlow;
|
||||
|
||||
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
||||
|
||||
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);
|
||||
if let Some(attr) = 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(
|
||||
cx,
|
||||
sig.decl,
|
||||
|
@ -143,7 +147,7 @@ fn check_must_use_candidate<'tcx>(
|
|||
diag.span_suggestion(
|
||||
fn_span,
|
||||
"add the attribute",
|
||||
format!("#[must_use] {}", snippet),
|
||||
format!("#[must_use] {snippet}"),
|
||||
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 {
|
||||
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 {
|
||||
let mut v = StaticMutVisitor {
|
||||
cx,
|
||||
mutates_static: false,
|
||||
};
|
||||
intravisit::walk_expr(&mut v, body.value);
|
||||
v.mutates_static
|
||||
for_each_expr(body.value, |e| {
|
||||
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
|
||||
|
||||
match e.kind {
|
||||
Call(_, args) => {
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId;
|
|||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
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 core::ops::ControlFlow;
|
||||
|
||||
use super::NOT_UNSAFE_PTR_ARG_DEREF;
|
||||
|
||||
pub(super) fn check_fn<'tcx>(
|
||||
|
@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>(
|
|||
body: &'tcx hir::Body<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
let expr = &body.value;
|
||||
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) {
|
||||
let raw_ptrs = iter_input_pats(decl, body)
|
||||
.filter_map(|arg| raw_ptr_arg(cx, arg))
|
||||
.collect::<HirIdSet>();
|
||||
|
||||
if !raw_ptrs.is_empty() {
|
||||
let typeck_results = cx.tcx.typeck_body(body.id());
|
||||
let mut v = DerefVisitor {
|
||||
cx,
|
||||
ptrs: raw_ptrs,
|
||||
typeck_results,
|
||||
};
|
||||
|
||||
intravisit::walk_expr(&mut v, expr);
|
||||
let typeck = cx.tcx.typeck_body(body.id());
|
||||
let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
|
||||
match e.kind {
|
||||
hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
|
||||
for arg in args {
|
||||
check_arg(cx, &raw_ptrs, arg);
|
||||
}
|
||||
},
|
||||
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> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
ptrs: HirIdSet,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
|
||||
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`",
|
||||
);
|
||||
}
|
||||
}
|
||||
fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
|
||||
if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
|
||||
span_lint(
|
||||
cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
arg.span,
|
||||
"this public function might dereference a raw pointer but is not marked `unsafe`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,10 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span,
|
|||
cx,
|
||||
TOO_MANY_ARGUMENTS,
|
||||
fn_span,
|
||||
&format!(
|
||||
"this function has too many arguments ({}/{})",
|
||||
args, too_many_arguments_threshold
|
||||
),
|
||||
&format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,10 +78,7 @@ pub(super) fn check_fn(
|
|||
cx,
|
||||
TOO_MANY_LINES,
|
||||
span,
|
||||
&format!(
|
||||
"this function has too many lines ({}/{})",
|
||||
line_count, too_many_lines_threshold
|
||||
),
|
||||
&format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
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::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -76,15 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind
|
||||
&& is_lang_ctor(cx, then_call_qpath, OptionSome)
|
||||
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
|
||||
&& is_lang_ctor(cx, qpath, OptionNone)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
|
||||
&& !stmts_contains_early_return(then_block.stmts)
|
||||
{
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({})", cond_snip)
|
||||
format!("({cond_snip})")
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
let mut method_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} 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) {
|
||||
"then_some"
|
||||
|
@ -102,14 +102,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
};
|
||||
|
||||
let help = format!(
|
||||
"consider using `bool::{}` like: `{}.{}({})`",
|
||||
method_name, cond_snip, method_name, method_body,
|
||||
"consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",
|
||||
);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_THEN_SOME_ELSE_NONE,
|
||||
expr.span,
|
||||
&format!("this could be simplified with `bool::{}`", method_name),
|
||||
&format!("this could be simplified with `bool::{method_name}`"),
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ use rustc_errors::Diagnostic;
|
|||
use rustc_hir as hir;
|
||||
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_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
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_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
|
@ -89,8 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
|||
(
|
||||
generics_suggestion_span,
|
||||
format!(
|
||||
"<{}{}S: ::std::hash::BuildHasher{}>",
|
||||
generics_snip,
|
||||
"<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
|
||||
if generics_snip.is_empty() { "" } else { ", " },
|
||||
if vis.suggestions.is_empty() {
|
||||
""
|
||||
|
@ -263,8 +262,8 @@ impl<'tcx> ImplicitHasherType<'tcx> {
|
|||
|
||||
fn type_arguments(&self) -> String {
|
||||
match *self {
|
||||
ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
|
||||
ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
|
||||
ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"),
|
||||
ImplicitHasherType::HashSet(.., ref t) => format!("{t}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ use clippy_utils::{
|
|||
diagnostics::span_lint_hir_and_then,
|
||||
get_async_fn_body, is_async_fn,
|
||||
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_hir::intravisit::{FnKind, Visitor};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -53,7 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
|
|||
span,
|
||||
"missing `return` statement",
|
||||
|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(
|
||||
break_span,
|
||||
"change `break` to `return` as shown",
|
||||
format!("return {}", snip),
|
||||
format!("return {snip}"),
|
||||
app,
|
||||
);
|
||||
},
|
||||
|
@ -152,7 +153,7 @@ fn lint_implicit_returns(
|
|||
|
||||
ExprKind::Loop(block, ..) => {
|
||||
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 dest.target_id.ok() == Some(expr.hir_id) {
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
|
@ -167,9 +168,8 @@ fn lint_implicit_returns(
|
|||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.visit_block(block);
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
if add_return {
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
if let Some(span) = call_site_span {
|
||||
|
|
114
clippy_lints/src/implicit_saturating_add.rs
Normal file
114
clippy_lints/src/implicit_saturating_add.rs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
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 rustc_ast::ast::LitKind;
|
||||
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>> {
|
||||
match peel_blocks_with_stmt(expr).kind {
|
||||
ExprKind::AssignOp(ref op1, target, value) => {
|
||||
if_chain! {
|
||||
if BinOpKind::Sub == op1.node;
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// Check if literal being subtracted is one
|
||||
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
|
||||
},
|
||||
ExprKind::Assign(target, value, _) => {
|
||||
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 let ExprKind::Lit(ref lit1) = right1.kind;
|
||||
if let LitKind::Int(1, _) = lit1.node;
|
||||
if is_integer_literal(right1, 1);
|
||||
then {
|
||||
Some(target)
|
||||
} else {
|
||||
|
@ -170,7 +160,7 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
|
|||
expr.span,
|
||||
"implicitly performing saturating subtraction",
|
||||
"try",
|
||||
format!("{} = {}.saturating_sub({});", var_name, var_name, '1'),
|
||||
format!("{var_name} = {var_name}.saturating_sub({});", '1'),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
let mut fields_snippet = String::new();
|
||||
let (last_ident, idents) = ordered_fields.split_last().unwrap();
|
||||
for ident in idents {
|
||||
let _ = write!(fields_snippet, "{}, ", ident);
|
||||
let _ = write!(fields_snippet, "{ident}, ");
|
||||
}
|
||||
fields_snippet.push_str(&last_ident.to_string());
|
||||
|
||||
|
@ -100,10 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
String::new()
|
||||
};
|
||||
|
||||
let sugg = format!("{} {{ {}{} }}",
|
||||
let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}",
|
||||
snippet(cx, qpath.span(), ".."),
|
||||
fields_snippet,
|
||||
base_snippet,
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -139,14 +139,14 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
|
|||
.map(|(index, _)| *index)
|
||||
.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() {
|
||||
let opt_ref = if slice.needs_ref { "ref " } else { "" };
|
||||
let pat_sugg_idents = (0..=*max_index)
|
||||
.map(|index| {
|
||||
if used_indices.contains(&index) {
|
||||
format!("{}{}", opt_ref, value_name(index))
|
||||
format!("{opt_ref}{}", value_name(index))
|
||||
} else {
|
||||
"_".to_string()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::higher;
|
||||
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_lint::{LateContext, LateLintPass};
|
||||
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::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
|
||||
ExprKind::Call(path, _) => path_def_id(cx, path)
|
||||
.map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
|
||||
.into(),
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
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(),
|
||||
_ => Finite,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
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 rustc_hir::{GenericParamKind, ImplItem, ImplItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
|||
}
|
||||
|
||||
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'
|
||||
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,
|
||||
item.span,
|
||||
&format!(
|
||||
"type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`",
|
||||
self_type
|
||||
"type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`"
|
||||
),
|
||||
None,
|
||||
&format!("remove the inherent method from type `{}`", self_type),
|
||||
&format!("remove the inherent method from type `{self_type}`"),
|
||||
);
|
||||
} else {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
INHERENT_TO_STRING,
|
||||
item.span,
|
||||
&format!(
|
||||
"implementation of inherent method `to_string(&self) -> String` for type `{}`",
|
||||
self_type
|
||||
),
|
||||
&format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"),
|
||||
None,
|
||||
&format!("implement trait `Display` for type `{}` instead", self_type),
|
||||
&format!("implement trait `Display` for type `{self_type}` instead"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
|
|||
cx,
|
||||
INLINE_FN_WITHOUT_BODY,
|
||||
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.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
|
||||
},
|
||||
|
|
|
@ -138,8 +138,8 @@ impl IntPlusOne {
|
|||
if let Some(snippet) = snippet_opt(cx, node.span) {
|
||||
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
|
||||
Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
|
|
|
@ -80,10 +80,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI
|
|||
cx,
|
||||
ITER_NOT_RETURNING_ITERATOR,
|
||||
sig.span,
|
||||
&format!(
|
||||
"this method is named `{}` but its return type does not implement `Iterator`",
|
||||
name
|
||||
),
|
||||
&format!("this method is named `{name}` but its return type does not implement `Iterator`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, ConstKind};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{BytePos, Pos, Span};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
|
@ -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();
|
||||
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 {
|
||||
Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref),
|
||||
Self::Option(_) => format!(
|
||||
"expected signature: `({}self) -> bool` or `({}self) -> Option<bool>",
|
||||
self_ref, self_ref
|
||||
),
|
||||
Self::Result(..) => format!(
|
||||
"expected signature: `({}self) -> bool` or `({}self) -> Result<bool>",
|
||||
self_ref, self_ref
|
||||
),
|
||||
Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"),
|
||||
Self::Option(_) => {
|
||||
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option<bool>")
|
||||
},
|
||||
Self::Result(..) => {
|
||||
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result<bool>")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,8 +325,7 @@ fn check_for_is_empty<'tcx>(
|
|||
let (msg, is_empty_span, self_kind) = match is_empty {
|
||||
None => (
|
||||
format!(
|
||||
"{} `{}` has a public `len` method, but no `is_empty` method",
|
||||
item_kind,
|
||||
"{item_kind} `{}` has a public `len` method, but no `is_empty` method",
|
||||
item_name.as_str(),
|
||||
),
|
||||
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()) => (
|
||||
format!(
|
||||
"{} `{}` has a public `len` method, but a private `is_empty` method",
|
||||
item_kind,
|
||||
"{item_kind} `{}` has a public `len` method, but a private `is_empty` method",
|
||||
item_name.as_str(),
|
||||
),
|
||||
Some(cx.tcx.def_span(is_empty.def_id)),
|
||||
|
@ -348,8 +345,7 @@ fn check_for_is_empty<'tcx>(
|
|||
{
|
||||
(
|
||||
format!(
|
||||
"{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
|
||||
item_kind,
|
||||
"{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
|
||||
item_name.as_str(),
|
||||
),
|
||||
Some(cx.tcx.def_span(is_empty.def_id)),
|
||||
|
@ -419,10 +415,9 @@ fn check_len(
|
|||
LEN_ZERO,
|
||||
span,
|
||||
&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!(
|
||||
"{}{}.is_empty()",
|
||||
op,
|
||||
"{op}{}.is_empty()",
|
||||
snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
|
@ -439,10 +434,9 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
|
|||
COMPARISON_TO_EMPTY,
|
||||
span,
|
||||
"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!(
|
||||
"{}{}.is_empty()",
|
||||
op,
|
||||
"{op}{}.is_empty()",
|
||||
snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
|
|
|
@ -106,8 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
// use mutably after the `if`
|
||||
|
||||
let sug = format!(
|
||||
"let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
|
||||
mut=mutability,
|
||||
"let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
|
||||
name=ident.name,
|
||||
cond=snippet(cx, cond.span, "_"),
|
||||
then=if then.stmts.len() > 1 { " ..;" } else { "" },
|
||||
|
|
|
@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
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_ENUM_CONSTRUCTOR),
|
||||
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(derive::DERIVE_HASH_XOR_EQ),
|
||||
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_names::DISALLOWED_NAMES),
|
||||
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::TOO_MANY_ARGUMENTS),
|
||||
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(infinite_iter::INFINITE_ITER),
|
||||
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(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_clamp::MANUAL_CLAMP),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
|
|
|
@ -22,6 +22,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(loops::MANUAL_FLATTEN),
|
||||
LintId::of(loops::SINGLE_ELEMENT_LOOP),
|
||||
LintId::of(loops::WHILE_LET_LOOP),
|
||||
LintId::of(manual_clamp::MANUAL_CLAMP),
|
||||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
|
|
|
@ -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_PATHS),
|
||||
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_MSRV_ATTR_IMPL),
|
||||
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
|
||||
LintId::of(utils::internal_lints::PRODUCE_ICE),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
|
||||
])
|
||||
|
|
|
@ -24,8 +24,6 @@ store.register_lints(&[
|
|||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
|
||||
|
@ -34,6 +32,8 @@ store.register_lints(&[
|
|||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_DEF_PATH,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
|
@ -60,6 +60,7 @@ store.register_lints(&[
|
|||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
box_default::BOX_DEFAULT,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
cargo::MULTIPLE_CRATE_VERSIONS,
|
||||
cargo::NEGATIVE_FEATURE_NAMES,
|
||||
|
@ -113,16 +114,17 @@ store.register_lints(&[
|
|||
derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
derive::UNSAFE_DERIVE_DESERIALIZE,
|
||||
disallowed_macros::DISALLOWED_MACROS,
|
||||
disallowed_methods::DISALLOWED_METHODS,
|
||||
disallowed_names::DISALLOWED_NAMES,
|
||||
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
|
||||
disallowed_types::DISALLOWED_TYPES,
|
||||
doc::DOC_LINK_WITH_QUOTES,
|
||||
doc::DOC_MARKDOWN,
|
||||
doc::MISSING_ERRORS_DOC,
|
||||
doc::MISSING_PANICS_DOC,
|
||||
doc::MISSING_SAFETY_DOC,
|
||||
doc::NEEDLESS_DOCTEST_MAIN,
|
||||
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
drop_forget_ref::DROP_COPY,
|
||||
drop_forget_ref::DROP_NON_DROP,
|
||||
|
@ -159,6 +161,7 @@ store.register_lints(&[
|
|||
format::USELESS_FORMAT,
|
||||
format_args::FORMAT_IN_FORMAT_ARGS,
|
||||
format_args::TO_STRING_IN_FORMAT_ARGS,
|
||||
format_args::UNINLINED_FORMAT_ARGS,
|
||||
format_impl::PRINT_IN_FORMAT_IMPL,
|
||||
format_impl::RECURSIVE_FORMAT_IMPL,
|
||||
format_push_string::FORMAT_PUSH_STRING,
|
||||
|
@ -182,6 +185,7 @@ store.register_lints(&[
|
|||
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
implicit_hasher::IMPLICIT_HASHER,
|
||||
implicit_return::IMPLICIT_RETURN,
|
||||
implicit_saturating_add::IMPLICIT_SATURATING_ADD,
|
||||
implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
|
||||
inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
|
||||
index_refutable_slice::INDEX_REFUTABLE_SLICE,
|
||||
|
@ -243,6 +247,7 @@ store.register_lints(&[
|
|||
manual_assert::MANUAL_ASSERT,
|
||||
manual_async_fn::MANUAL_ASYNC_FN,
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_clamp::MANUAL_CLAMP,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_rem_euclid::MANUAL_REM_EUCLID,
|
||||
|
|
|
@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
|
||||
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
|
||||
LintId::of(copies::BRANCHES_SHARING_CODE),
|
||||
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
|
||||
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
|
||||
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
|
||||
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
|
||||
|
@ -30,6 +31,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||
LintId::of(unused_peekable::UNUSED_PEEKABLE),
|
||||
LintId::of(unused_rounding::UNUSED_ROUNDING),
|
||||
|
|
|
@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(dereference::REF_BINDING_TO_REFERENCE),
|
||||
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
|
||||
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
|
||||
LintId::of(doc::DOC_LINK_WITH_QUOTES),
|
||||
LintId::of(doc::DOC_MARKDOWN),
|
||||
LintId::of(doc::MISSING_ERRORS_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(enum_variants::MODULE_NAME_REPETITIONS),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
|
||||
LintId::of(excessive_bools::FN_PARAMS_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::TOO_MANY_LINES),
|
||||
LintId::of(if_not_else::IF_NOT_ELSE),
|
||||
|
@ -88,8 +89,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
|
||||
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
|
||||
LintId::of(strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(types::LINKEDLIST),
|
||||
LintId::of(types::OPTION_OPTION),
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(box_default::BOX_DEFAULT),
|
||||
LintId::of(entry::MAP_ENTRY),
|
||||
LintId::of(escape::BOXED_LOCAL),
|
||||
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
||||
|
|
|
@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
|
||||
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_names::DISALLOWED_NAMES),
|
||||
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::MUST_USE_UNIT),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
|
||||
LintId::of(inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
|
||||
LintId::of(len_zero::COMPARISON_TO_EMPTY),
|
||||
|
|
|
@ -31,6 +31,7 @@ extern crate rustc_data_structures;
|
|||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_hir_analysis;
|
||||
extern crate rustc_hir_pretty;
|
||||
extern crate rustc_index;
|
||||
extern crate rustc_infer;
|
||||
|
@ -43,7 +44,6 @@ extern crate rustc_session;
|
|||
extern crate rustc_span;
|
||||
extern crate rustc_target;
|
||||
extern crate rustc_trait_selection;
|
||||
extern crate rustc_hir_analysis;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clippy_utils;
|
||||
|
@ -180,6 +180,7 @@ mod bool_assert_comparison;
|
|||
mod bool_to_int_with_if;
|
||||
mod booleans;
|
||||
mod borrow_deref_ref;
|
||||
mod box_default;
|
||||
mod cargo;
|
||||
mod casts;
|
||||
mod checked_conversions;
|
||||
|
@ -198,12 +199,12 @@ mod default_union_representation;
|
|||
mod dereference;
|
||||
mod derivable_impls;
|
||||
mod derive;
|
||||
mod disallowed_macros;
|
||||
mod disallowed_methods;
|
||||
mod disallowed_names;
|
||||
mod disallowed_script_idents;
|
||||
mod disallowed_types;
|
||||
mod doc;
|
||||
mod doc_link_with_quotes;
|
||||
mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
mod duplicate_mod;
|
||||
|
@ -238,6 +239,7 @@ mod if_not_else;
|
|||
mod if_then_some_else_none;
|
||||
mod implicit_hasher;
|
||||
mod implicit_return;
|
||||
mod implicit_saturating_add;
|
||||
mod implicit_saturating_sub;
|
||||
mod inconsistent_struct_constructor;
|
||||
mod index_refutable_slice;
|
||||
|
@ -267,6 +269,7 @@ mod main_recursion;
|
|||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_clamp;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
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| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(&format!(
|
||||
"error reading Clippy's configuration file. `{}` is not a valid Rust version",
|
||||
s
|
||||
"error reading Clippy's configuration file. `{s}` is not a valid Rust version"
|
||||
));
|
||||
None
|
||||
})
|
||||
|
@ -433,8 +435,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
|
|||
let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(&format!(
|
||||
"error reading Clippy's configuration file. `{}` is not a valid Rust version",
|
||||
s
|
||||
"error reading Clippy's configuration file. `{s}` is not a valid Rust version"
|
||||
));
|
||||
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 clippy_msrv != cargo_msrv {
|
||||
sess.warn(&format!(
|
||||
"the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
|
||||
clippy_msrv
|
||||
"the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -465,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf {
|
|||
Ok(Some(path)) => path,
|
||||
Ok(None) => return Conf::default(),
|
||||
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();
|
||||
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::IfChainStyle));
|
||||
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::new(utils::internal_lints::LintWithoutLintPass::default()));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::InterningDefinedSymbol>::default());
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::LintWithoutLintPass>::default());
|
||||
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::MsrvAttrImpl));
|
||||
}
|
||||
|
@ -629,10 +629,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
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(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(entry::HashMapPass));
|
||||
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(swap::Swap));
|
||||
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<_>>();
|
||||
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
|
||||
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(infinite_iter::InfiniteIter));
|
||||
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(fallible_impl_from::FallibleImplFrom));
|
||||
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,
|
||||
))
|
||||
});
|
||||
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(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
|
||||
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));
|
||||
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(|_| 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(move |_| Box::new(dereference::Dereferencing::new(msrv)));
|
||||
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<_>>();
|
||||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_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(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
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();
|
||||
store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
|
||||
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::StringToString));
|
||||
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(from_str_radix_10::FromStrRadix10));
|
||||
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(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_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
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_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
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::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
|
||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||
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)));
|
||||
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,
|
||||
})
|
||||
});
|
||||
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(empty_structs_with_brackets::EmptyStructsWithBrackets));
|
||||
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(|_| Box::new(strings::TrimSplitWhitespace));
|
||||
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(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
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;
|
||||
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(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(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(unused_peekable::UnusedPeekable));
|
||||
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(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`
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ use rustc_hir::intravisit::{
|
|||
use rustc_hir::FnRetTy::Return;
|
||||
use rustc_hir::{
|
||||
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin,
|
||||
TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
|
||||
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn,
|
||||
TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
||||
|
@ -276,7 +276,7 @@ fn could_use_elision<'tcx>(
|
|||
let mut checker = BodyLifetimeChecker {
|
||||
lifetimes_used_in_body: false,
|
||||
};
|
||||
checker.visit_expr(&body.value);
|
||||
checker.visit_expr(body.value);
|
||||
if checker.lifetimes_used_in_body {
|
||||
return false;
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||
sub_visitor.visit_fn_decl(decl);
|
||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||
},
|
||||
TyKind::TraitObject(bounds, ref lt, _) => {
|
||||
TyKind::TraitObject(bounds, lt, _) => {
|
||||
if !lt.is_elided() {
|
||||
self.unelided_trait_object_lifetime = true;
|
||||
}
|
||||
|
|
|
@ -478,7 +478,7 @@ impl DecimalLiteralRepresentation {
|
|||
if num_lit.radix == Radix::Decimal;
|
||||
if val >= u128::from(self.threshold);
|
||||
then {
|
||||
let hex = format!("{:#X}", val);
|
||||
let hex = format!("{val:#X}");
|
||||
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
|
||||
let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
|
||||
warning_type.display(num_lit.format(), cx, lit.span);
|
||||
|
|
|
@ -44,11 +44,10 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
EXPLICIT_COUNTER_LOOP,
|
||||
span,
|
||||
&format!("the variable `{}` is used as a loop counter", name),
|
||||
&format!("the variable `{name}` is used as a loop counter"),
|
||||
"consider using",
|
||||
format!(
|
||||
"for ({}, {}) in {}.enumerate()",
|
||||
name,
|
||||
"for ({name}, {}) in {}.enumerate()",
|
||||
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
|
||||
make_iterator_snippet(cx, arg, &mut applicability),
|
||||
),
|
||||
|
@ -65,24 +64,21 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
EXPLICIT_COUNTER_LOOP,
|
||||
span,
|
||||
&format!("the variable `{}` is used as a loop counter", name),
|
||||
&format!("the variable `{name}` is used as a loop counter"),
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"consider using",
|
||||
format!(
|
||||
"for ({}, {}) in (0_{}..).zip({})",
|
||||
name,
|
||||
"for ({name}, {}) in (0_{int_name}..).zip({})",
|
||||
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
|
||||
int_name,
|
||||
make_iterator_snippet(cx, arg, &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
|
||||
diag.note(&format!(
|
||||
"`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
|
||||
name, int_name
|
||||
"`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
|
||||
));
|
||||
},
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue