Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2022-10-06 09:19:29 +02:00
commit e2808afd60
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
621 changed files with 10302 additions and 4445 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,10 +30,7 @@ pub fn install_hook(force_override: bool) {
println!("info: the hook can be removed with `cargo dev remove git-hook`");
println!("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

View file

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

View file

@ -17,10 +17,7 @@ pub fn install_tasks(force_override: bool) {
println!("info: the task file can be removed with `cargo dev remove vscode-tasks`");
println!("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;
}

View file

@ -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, its 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>) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b
}
fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVer
let turbofish = match &cast_to_hir_ty.kind {
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,
);
}

View file

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

View file

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

View file

@ -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,
_ => {},
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{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)"));
}
});
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,8 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
use clippy_utils::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
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -142,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
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,
);
}

View file

@ -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);
},
[..] => {},

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{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,
);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -210,7 +210,8 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
}
}
if cx.access_levels.is_exported(visited_trait.def_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
if cx.access_levels.is_exported(visited_trait.def_id.def_id)
&& trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
{
let mut current_and_super_traits = DefIdSet::default();
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,

View file

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

View file

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

View file

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

View file

@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::INVALID_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),
])

View file

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

View file

@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(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),

View file

@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(dereference::REF_BINDING_TO_REFERENCE),
LintId::of(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),

View file

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

View file

@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(default_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),

View file

@ -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(&macro_matcher)));
store.register_late_pass(|_| Box::new(macro_use::MacroUseImports::default()));
store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
store.register_late_pass(|_| Box::new(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`
}

View file

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

View file

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

View file

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